I18n через jsonb-поля

This commit is contained in:
2025-04-24 15:53:05 +04:00
parent 35b571eb57
commit 31ad99ade5
33 changed files with 409 additions and 264 deletions
@@ -1,6 +1,7 @@
using Konsole; using Konsole;
using Pf2eDataScrapper.Importers.Implementations.Aonprd.Compendium; 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; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder;
@@ -7,13 +7,15 @@ using Pf2eModel.Model;
using Pf2eModel.Model.Base; using Pf2eModel.Model.Base;
using Pf2eModel.Model.Base.Source; using Pf2eModel.Model.Base.Source;
using Pf2eModel.Model.Base.Targets; using Pf2eModel.Model.Base.Targets;
using Pf2eModel.Model.Common.I18n;
using Pf2eModel.Model.Enum; using Pf2eModel.Model.Enum;
using Pf2eModel.Model.Structure; using Pf2eModel.Model.Structure;
using System.Collections.Generic;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors;
internal abstract partial class AbstractProcessor(AonprdProcessorInput input) internal abstract partial class AbstractProcessor(AonprdProcessorInput input)
{ {
@@ -65,11 +67,11 @@ internal abstract partial class AbstractProcessor(AonprdProcessorInput input)
{ {
Entity.Id = Helper.NextEntityId; Entity.Id = Helper.NextEntityId;
Entity.Type = Type; Entity.Type = Type;
Entity.Name = Input.GetRequired<string>("name"); Entity.Name[LocaleType.English] = Input.GetRequired<string>("name");
Entity.Img = ""; Entity.Img = "";
Entity.Description = GetDescription(); Entity.Description = GetDescription();
Entity.Rarity = Input.EnumOptional<EntityRarity>("rarity", EntityRarity.Common); Entity.Rarity = Input.EnumOptional<EntityRarity>("rarity", EntityRarity.Common);
Entity.Slug = Helper.GenerateSlug(Entity.Name); Entity.Slug = Helper.GenerateSlug(Entity.Name[LocaleType.English]);
Entity.IsTranslated = false; Entity.IsTranslated = false;
Entity.IsChecked = false; Entity.IsChecked = false;
@@ -81,15 +83,6 @@ internal abstract partial class AbstractProcessor(AonprdProcessorInput input)
protected virtual void AddAlias() protected virtual void AddAlias()
{ {
EntityAlias alias = new()
{
Type = AliasType.English,
Alias = Entity.Name,
Entity = Entity
};
Context.EntityAliases.Add(alias);
JArray? legacyIdArray = Input.GetOptional<JArray>("legacy_id"); JArray? legacyIdArray = Input.GetOptional<JArray>("legacy_id");
if (legacyIdArray is null) if (legacyIdArray is null)
@@ -101,17 +94,8 @@ internal abstract partial class AbstractProcessor(AonprdProcessorInput input)
if (LegacyAliases.TryGetValue(legacyId, out string? legacyName)) if (LegacyAliases.TryGetValue(legacyId, out string? legacyName))
{ {
if (legacyName != Entity.Name) // if (legacyName != Entity.Name[LocaleType.English])
{ Entity.Name[LocaleType.LegacyEnglish] = legacyName;
EntityAlias legacyAlias = new()
{
Type = AliasType.LegacyEnglish,
Alias = legacyName,
Entity = Entity
};
Context.EntityAliases.Add(legacyAlias);
}
LegacyAliases.Remove(legacyId); LegacyAliases.Remove(legacyId);
} }
@@ -187,20 +171,18 @@ internal abstract partial class AbstractProcessor(AonprdProcessorInput input)
IEnumerable<string> names = traitsJson.ToArray().Select(jToken => jToken.ToString()); IEnumerable<string> names = traitsJson.ToArray().Select(jToken => jToken.ToString());
List<Entity> traitEntities = Context.Traits List<Entity> traitEntities = Context.Traits
.SelectMany(t => t.Entity.Aliases) .Select(t => t.Entity)
.Where(a => names.Contains(a.Alias)) .Where(e => Pf2eDbContext.LocaleSearchArray(e.Name, names))
.Select(a => a.Entity)
.ToList(); .ToList();
List<Trait> traits = Context.Traits List<Trait> traits = Context.Traits
.Include(t => t.Entity) .Include(t => t.Entity)
.ThenInclude(e => e.Aliases)
.Where(t => traitEntities.Contains(t.Entity)) .Where(t => traitEntities.Contains(t.Entity))
.ToList(); .ToList();
foreach (string name in names) 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!!!"); 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 (RemasterWithoutLegacy.TryGetValue(id, out Entity? remastered))
{ {
if (remastered.Name == name) // if (remastered.ToString() != name)
{ remastered.Name[LocaleType.LegacyEnglish] = name;
return;
}
EntityAlias alias = new() RemasterWithoutLegacy.Remove(id);
{
Type = AliasType.LegacyEnglish,
Alias = name,
Entity = remastered!
};
Context.EntityAliases.Add(alias);
} }
else else
{ {
@@ -1,4 +1,4 @@
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors;
[AttributeUsage(AttributeTargets.Class)] [AttributeUsage(AttributeTargets.Class)]
public class AonprdProcessorAttribute(string name, string type, string[] dependencies) : Attribute public class AonprdProcessorAttribute(string name, string type, string[] dependencies) : Attribute
@@ -1,6 +1,6 @@
using System.Reflection; using System.Reflection;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors;
internal class AonprdProcessorCollection internal class AonprdProcessorCollection
{ {
@@ -2,7 +2,7 @@
using Pf2eModel.Model; using Pf2eModel.Model;
using Pf2eModel.Model.Enum; using Pf2eModel.Model.Enum;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types;
[AonprdProcessor("action")] [AonprdProcessor("action")]
internal class ActionProcessor(AonprdProcessorInput input) : AbstractProcessor(input) internal class ActionProcessor(AonprdProcessorInput input) : AbstractProcessor(input)
@@ -6,7 +6,7 @@ using Pf2eModel.Model.Structure;
using Pf2eModel.Model.Structure.AncestryRelation; using Pf2eModel.Model.Structure.AncestryRelation;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types;
[AonprdProcessor("ancestry", ["trait", "language"])] [AonprdProcessor("ancestry", ["trait", "language"])]
internal class AncestryProcessor(AonprdProcessorInput input) : AbstractProcessor(input) internal class AncestryProcessor(AonprdProcessorInput input) : AbstractProcessor(input)
@@ -6,7 +6,7 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types;
[AonprdProcessor("archetype")] [AonprdProcessor("archetype")]
internal class ArchetypeProcessor(AonprdProcessorInput input) : AbstractProcessor(input) internal class ArchetypeProcessor(AonprdProcessorInput input) : AbstractProcessor(input)
@@ -23,7 +23,6 @@ internal class ArchetypeProcessor(AonprdProcessorInput input) : AbstractProcesso
Object.Level = Input.GetOptional<int>("level"); Object.Level = Input.GetOptional<int>("level");
} }
protected override string GetDescription() protected override string GetDescription()
@@ -9,7 +9,7 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types;
[AonprdProcessor("background")] [AonprdProcessor("background")]
internal class BackgroundProcessor(AonprdProcessorInput input) : AbstractProcessor(input) internal class BackgroundProcessor(AonprdProcessorInput input) : AbstractProcessor(input)
@@ -14,7 +14,7 @@ using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types;
[AonprdProcessor("class", ["trait", "source", "skill"])] [AonprdProcessor("class", ["trait", "source", "skill"])]
internal class ClassProcessor(AonprdProcessorInput input) : AbstractProcessor(input) internal class ClassProcessor(AonprdProcessorInput input) : AbstractProcessor(input)
@@ -89,6 +89,7 @@ internal class ClassProcessor(AonprdProcessorInput input) : AbstractProcessor(in
.ToArray(); .ToArray();
Training[] skills = Context.Trainings.Where(t => skillNames.Contains(t.Name)).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() return skills.Select(skill => new ClassTraining()
{ {
@@ -1,8 +1,9 @@
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using Pf2eModel;
using Pf2eModel.Model.Enum; using Pf2eModel.Model.Enum;
using Pf2eModel.Model.Structure; using Pf2eModel.Model.Structure;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types;
[AonprdProcessor("feat", ["trait", "source", "archetype"])] [AonprdProcessor("feat", ["trait", "source", "archetype"])]
internal class FeatProcessor(AonprdProcessorInput input) : AbstractProcessor(input) internal class FeatProcessor(AonprdProcessorInput input) : AbstractProcessor(input)
@@ -42,7 +43,10 @@ internal class FeatProcessor(AonprdProcessorInput input) : AbstractProcessor(inp
string[] archetypeStrings = Input.GetOptional<JArray>("archetype")?.Select(t => t.ToString()).ToArray() ?? []; string[] archetypeStrings = Input.GetOptional<JArray>("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) if (archetypes.Length != 0)
{ {
@@ -53,7 +57,7 @@ internal class FeatProcessor(AonprdProcessorInput input) : AbstractProcessor(inp
{ {
Object.Archetypes.Add(archetype); Object.Archetypes.Add(archetype);
if (Entity.Name.Contains("Dedication")) if (Entity.Name.Any(l => l.Value.Contains("Dedication")))
{ {
archetype.DedicationFeat = Object; archetype.DedicationFeat = Object;
} }
@@ -1,7 +1,7 @@
using Pf2eModel.Model.Enum; using Pf2eModel.Model.Enum;
using Pf2eModel.Model.Structure; using Pf2eModel.Model.Structure;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types;
[AonprdProcessor("language")] [AonprdProcessor("language")]
internal class LanguageProcessor(AonprdProcessorInput input) : AbstractProcessor(input) internal class LanguageProcessor(AonprdProcessorInput input) : AbstractProcessor(input)
@@ -8,7 +8,7 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types;
[AonprdProcessor("source", new string[] { })] [AonprdProcessor("source", new string[] { })]
internal class SourceProcessor(AonprdProcessorInput input) : AbstractProcessor(input) internal class SourceProcessor(AonprdProcessorInput input) : AbstractProcessor(input)
@@ -8,7 +8,7 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types;
[AonprdProcessor("skill", new string[] { })] [AonprdProcessor("skill", new string[] { })]
internal class TrainingProcessor(AonprdProcessorInput input) : AbstractProcessor(input) internal class TrainingProcessor(AonprdProcessorInput input) : AbstractProcessor(input)
@@ -1,7 +1,7 @@
using Pf2eModel.Model.Enum; using Pf2eModel.Model.Enum;
using Pf2eModel.Model.Structure; using Pf2eModel.Model.Structure;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types;
[AonprdProcessor("trait", new string[] { })] [AonprdProcessor("trait", new string[] { })]
internal class TraitProcessor(AonprdProcessorInput input) : AbstractProcessor(input) internal class TraitProcessor(AonprdProcessorInput input) : AbstractProcessor(input)
@@ -4,6 +4,7 @@ using Pf2eModel.Model;
using Pf2eModel.Model.Base; using Pf2eModel.Model.Base;
using Pf2eModel.Model.Base.Source; using Pf2eModel.Model.Base.Source;
using Pf2eModel.Model.Base.Targets; using Pf2eModel.Model.Base.Targets;
using Pf2eModel.Model.Common.I18n;
using Pf2eModel.Model.Enum; using Pf2eModel.Model.Enum;
using Pf2eModel.Model.Structure; using Pf2eModel.Model.Structure;
using System.Security.Cryptography; using System.Security.Cryptography;
@@ -30,14 +31,14 @@ internal abstract class AbstractProcessor(ProcessorInput input)
Entity.Id = Helper.NextEntityId; Entity.Id = Helper.NextEntityId;
Entity.Type = Type; Entity.Type = Type;
Entity.Name = Input.GetRequired<string>("name"); Entity.Name[LocaleType.English] = Input.GetRequired<string>("name");
Entity.Img = Input.GetRequired<string>("img"); Entity.Img = Input.GetRequired<string>("img");
Entity.Description = Input.GetRequired<string>("system.description.value"); Entity.Description = Input.GetRequired<string>("system.description.value");
Entity.IsTranslated = false; Entity.IsTranslated = false;
Entity.IsChecked = false; Entity.IsChecked = false;
Entity.Slug = Helper.GenerateSlug(Entity.Name); Entity.Slug = Helper.GenerateSlug(Entity.Name[LocaleType.English]);
Entity.Rarity = Input.EnumOptional<EntityRarity>("system.traits.rarity", EntityRarity.Common); Entity.Rarity = Input.EnumOptional<EntityRarity>("system.traits.rarity", EntityRarity.Common);
Object.Id = Entity.Id; Object.Id = Entity.Id;
@@ -45,15 +46,16 @@ internal abstract class AbstractProcessor(ProcessorInput input)
Context.Entities.Add(Entity); Context.Entities.Add(Entity);
// Alias // Alias
/*
EntityAlias alias = new() EntityAlias alias = new()
{ {
Type = AliasType.English, Type = LocaleType.English,
Alias = Entity.Name, Alias = Entity.Name,
EntityId = Entity.Id, EntityId = Entity.Id,
Entity = Entity Entity = Entity
}; };
Context.EntityAliases.Add(alias); Context.EntityAliases.Add(alias);
*/
// Source // Source
EntitySource source = new() EntitySource source = new()
{ {
@@ -138,11 +140,11 @@ internal abstract class AbstractProcessor(ProcessorInput input)
protected IEnumerable<Trait> GetTraits(IEnumerable<string> names) protected IEnumerable<Trait> GetTraits(IEnumerable<string> names)
{ {
List<Trait> result = Context.Traits.Where(t => names.Contains(t.Entity.Name)).ToList(); List<Trait> result = Context.Traits.Where(t => names.Contains(t.Entity.ToString())).ToList();
foreach (string name in names) foreach (string name in names)
{ {
if (result.Any(t => t.Entity.Name == name)) if (result.Any(t => t.Entity.Name.Contains(name)))
{ {
continue; continue;
} }
+4
View File
@@ -37,4 +37,8 @@
</None> </None>
</ItemGroup> </ItemGroup>
<ItemGroup>
<Folder Include="Importers\Implementations\Aonprd\DataBuilder\Processors\Subclasses\" />
</ItemGroup>
</Project> </Project>
+58 -22
View File
@@ -1,4 +1,8 @@
using Microsoft.EntityFrameworkCore.Migrations; 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 #nullable disable
@@ -22,46 +26,76 @@ BEGIN
|| ' CASCADE;' || ' CASCADE;'
FROM pg_tables FROM pg_tables
WHERE schemaname = 'public' WHERE schemaname = 'public'
AND tablename != '__EFMigrationsHistory' AND tablename != '__EFMigrationsHistory'
); );
END END
$func$; $func$;
"""); """);
migrationBuilder.Sql(""" migrationBuilder.Sql("""
CREATE OR REPLACE FUNCTION f_drop_tables() CREATE OR REPLACE FUNCTION f_drop_tables()
RETURNS void RETURNS void
LANGUAGE plpgsql AS LANGUAGE plpgsql AS
$func$ $func$
BEGIN BEGIN
EXECUTE EXECUTE
(SELECT 'DROP TABLE ' (SELECT 'DROP TABLE '
|| string_agg(format('%I.%I', schemaname, tablename), ', ') || string_agg(format('%I.%I', schemaname, tablename), ', ')
|| ' CASCADE;' || ' CASCADE;'
FROM pg_tables FROM pg_tables
WHERE schemaname = 'public' WHERE schemaname = 'public'
); );
END END
$func$; $func$;
"""); """);
migrationBuilder.Sql(""" migrationBuilder.Sql("""
CREATE OR REPLACE FUNCTION f_permission_tables(_username text) CREATE OR REPLACE FUNCTION f_permission_tables(_username text)
RETURNS void RETURNS void
LANGUAGE plpgsql AS LANGUAGE plpgsql AS
$func$ $func$
DECLARE DECLARE
_tbl text; _tbl text;
_sch text; _sch text;
BEGIN BEGIN
FOR _sch, _tbl IN FOR _sch, _tbl IN
SELECT schemaname, tablename SELECT schemaname, tablename
FROM pg_tables FROM pg_tables
WHERE schemaname = 'public' WHERE schemaname = 'public'
LOOP LOOP
EXECUTE format('ALTER TABLE %I.%I OWNER TO %I', _sch, _tbl, _username); EXECUTE format('ALTER TABLE %I.%I OWNER TO %I', _sch, _tbl, _username);
END LOOP; END LOOP;
END END
$func$; $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$;
"""
);
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -70,5 +104,7 @@ $func$;
migrationBuilder.Sql("DROP FUNCTION f_truncate_tables;"); migrationBuilder.Sql("DROP FUNCTION f_truncate_tables;");
migrationBuilder.Sql("DROP FUNCTION f_drop_tables;"); migrationBuilder.Sql("DROP FUNCTION f_drop_tables;");
migrationBuilder.Sql("DROP FUNCTION f_permission_tables;"); migrationBuilder.Sql("DROP FUNCTION f_permission_tables;");
migrationBuilder.Sql("DROP FUNCTION locale_search;");
migrationBuilder.Sql("DROP FUNCTION locale_search_array;");
} }
} }
@@ -13,7 +13,7 @@ using Pf2eModel.Model.Enum;
namespace Pf2eModel.Migrations namespace Pf2eModel.Migrations
{ {
[DbContext(typeof(Pf2eDbContext))] [DbContext(typeof(Pf2eDbContext))]
[Migration("20250421123520_1")] [Migration("20250424112427_1")]
partial class _1 partial class _1
{ {
/// <inheritdoc /> /// <inheritdoc />
@@ -25,7 +25,6 @@ namespace Pf2eModel.Migrations
.HasAnnotation("Relational:MaxIdentifierLength", 63); .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, "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, "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, "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" }); 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, "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_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, "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, "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, "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" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "spell_area_type", new[] { "burst", "emanation", "cone", "line", "square", "cube", "cylinder" });
@@ -142,8 +142,7 @@ namespace Pf2eModel.Migrations
b.Property<string>("Name") b.Property<string>("Name")
.IsRequired() .IsRequired()
.HasMaxLength(255) .HasColumnType("jsonb")
.HasColumnType("character varying(255)")
.HasColumnName("name") .HasColumnName("name")
.HasColumnOrder(2) .HasColumnOrder(2)
.HasComment("Отображаемое имя"); .HasComment("Отображаемое имя");
@@ -173,31 +172,6 @@ namespace Pf2eModel.Migrations
b.ToTable("entities", (string)null); b.ToTable("entities", (string)null);
}); });
modelBuilder.Entity("Pf2eModel.Model.Base.EntityAlias", b =>
{
b.Property<int>("EntityId")
.HasColumnType("integer")
.HasColumnName("entity_id")
.HasColumnOrder(1);
b.Property<AliasType>("Type")
.HasColumnType("alias_type")
.HasColumnName("type")
.HasColumnOrder(2);
b.Property<string>("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 => modelBuilder.Entity("Pf2eModel.Model.Base.Keywords.Keyword", b =>
{ {
b.Property<int>("Id") b.Property<int>("Id")
@@ -1536,18 +1510,6 @@ namespace Pf2eModel.Migrations
.HasConstraintName("fk_entity_trait_traits_traits_id"); .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 => modelBuilder.Entity("Pf2eModel.Model.Base.Keywords.KeywordAlias", b =>
{ {
b.HasOne("Pf2eModel.Model.Base.Keywords.Keyword", "Keyword") b.HasOne("Pf2eModel.Model.Base.Keywords.Keyword", "Keyword")
@@ -2056,8 +2018,6 @@ namespace Pf2eModel.Migrations
modelBuilder.Entity("Pf2eModel.Model.Base.Entity", b => modelBuilder.Entity("Pf2eModel.Model.Base.Entity", b =>
{ {
b.Navigation("Aliases");
b.Navigation("Externals"); b.Navigation("Externals");
b.Navigation("Sources"); b.Navigation("Sources");
@@ -15,7 +15,6 @@ namespace Pf2eModel.Migrations
{ {
migrationBuilder.AlterDatabase() 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: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:ancestry_vision", "normal,low-light-vision,dark-vision")
.Annotation("Npgsql:Enum:attribute_values", "free,str,dex,con,int,wis,cha") .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") .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: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_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: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:size", "tiny,small,medium,large")
.Annotation("Npgsql:Enum:source_category", "rulebook,adventure-path,lost-omens,adventures,comics,blog-posts,society") .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") .Annotation("Npgsql:Enum:spell_area_type", "burst,emanation,cone,line,square,cube,cylinder")
@@ -41,7 +41,7 @@ namespace Pf2eModel.Migrations
{ {
id = table.Column<int>(type: "integer", nullable: false) id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
name = table.Column<string>(type: "character varying(255)", maxLength: 255, nullable: false, comment: "Отображаемое имя"), name = table.Column<string>(type: "jsonb", nullable: false, comment: "Отображаемое имя"),
type = table.Column<EntityType>(type: "entity_type", nullable: false, comment: "Тип объекта"), type = table.Column<EntityType>(type: "entity_type", nullable: false, comment: "Тип объекта"),
slug = table.Column<string>(type: "character varying(255)", maxLength: 255, nullable: false, comment: "Часть имени в URL"), slug = table.Column<string>(type: "character varying(255)", maxLength: 255, nullable: false, comment: "Часть имени в URL"),
img = table.Column<string>(type: "character varying(255)", maxLength: 255, nullable: false, comment: "Изображение объекта"), img = table.Column<string>(type: "character varying(255)", maxLength: 255, nullable: false, comment: "Изображение объекта"),
@@ -250,25 +250,6 @@ namespace Pf2eModel.Migrations
onDelete: ReferentialAction.Cascade); onDelete: ReferentialAction.Cascade);
}); });
migrationBuilder.CreateTable(
name: "entity_aliases",
columns: table => new
{
entity_id = table.Column<int>(type: "integer", nullable: false),
type = table.Column<AliasType>(type: "alias_type", nullable: false),
alias = table.Column<string>(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( migrationBuilder.CreateTable(
name: "feats", name: "feats",
columns: table => new columns: table => new
@@ -1189,9 +1170,6 @@ namespace Pf2eModel.Migrations
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "deity_weapons"); name: "deity_weapons");
migrationBuilder.DropTable(
name: "entity_aliases");
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "entity_externals"); name: "entity_externals");
@@ -22,7 +22,6 @@ namespace Pf2eModel.Migrations
.HasAnnotation("Relational:MaxIdentifierLength", 63); .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, "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, "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, "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" }); 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, "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_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, "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, "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, "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" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "spell_area_type", new[] { "burst", "emanation", "cone", "line", "square", "cube", "cylinder" });
@@ -139,8 +139,7 @@ namespace Pf2eModel.Migrations
b.Property<string>("Name") b.Property<string>("Name")
.IsRequired() .IsRequired()
.HasMaxLength(255) .HasColumnType("jsonb")
.HasColumnType("character varying(255)")
.HasColumnName("name") .HasColumnName("name")
.HasColumnOrder(2) .HasColumnOrder(2)
.HasComment("Отображаемое имя"); .HasComment("Отображаемое имя");
@@ -170,31 +169,6 @@ namespace Pf2eModel.Migrations
b.ToTable("entities", (string)null); b.ToTable("entities", (string)null);
}); });
modelBuilder.Entity("Pf2eModel.Model.Base.EntityAlias", b =>
{
b.Property<int>("EntityId")
.HasColumnType("integer")
.HasColumnName("entity_id")
.HasColumnOrder(1);
b.Property<AliasType>("Type")
.HasColumnType("alias_type")
.HasColumnName("type")
.HasColumnOrder(2);
b.Property<string>("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 => modelBuilder.Entity("Pf2eModel.Model.Base.Keywords.Keyword", b =>
{ {
b.Property<int>("Id") b.Property<int>("Id")
@@ -1533,18 +1507,6 @@ namespace Pf2eModel.Migrations
.HasConstraintName("fk_entity_trait_traits_traits_id"); .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 => modelBuilder.Entity("Pf2eModel.Model.Base.Keywords.KeywordAlias", b =>
{ {
b.HasOne("Pf2eModel.Model.Base.Keywords.Keyword", "Keyword") b.HasOne("Pf2eModel.Model.Base.Keywords.Keyword", "Keyword")
@@ -2053,8 +2015,6 @@ namespace Pf2eModel.Migrations
modelBuilder.Entity("Pf2eModel.Model.Base.Entity", b => modelBuilder.Entity("Pf2eModel.Model.Base.Entity", b =>
{ {
b.Navigation("Aliases");
b.Navigation("Externals"); b.Navigation("Externals");
b.Navigation("Sources"); b.Navigation("Sources");
+2 -4
View File
@@ -2,6 +2,7 @@
using Pf2eModel.Model.Base.Keywords; using Pf2eModel.Model.Base.Keywords;
using Pf2eModel.Model.Base.Source; using Pf2eModel.Model.Base.Source;
using Pf2eModel.Model.Base.Targets; using Pf2eModel.Model.Base.Targets;
using Pf2eModel.Model.Common.I18n;
using Pf2eModel.Model.Enum; using Pf2eModel.Model.Enum;
using Pf2eModel.Model.Structure; using Pf2eModel.Model.Structure;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
@@ -16,9 +17,8 @@ public class Entity
public int Id { get; set; } public int Id { get; set; }
[Column(Order = 2)] [Column(Order = 2)]
[MaxLength(Pf2eDbContext.NameLength)]
[Comment("Отображаемое имя")] [Comment("Отображаемое имя")]
public string Name { get; set; } = null!; public LocaleString Name { get; set; } = new();
[Column(Order = 3)] [Column(Order = 3)]
[Comment("Тип объекта")] [Comment("Тип объекта")]
@@ -53,8 +53,6 @@ public class Entity
public virtual ICollection<Keyword> Keywords { get; set; } = []; public virtual ICollection<Keyword> Keywords { get; set; } = [];
public virtual ICollection<EntityAlias> Aliases { get; set; } = [];
public virtual ICollection<EntitySource> Sources { get; set; } = []; public virtual ICollection<EntitySource> Sources { get; set; } = [];
public virtual ICollection<EntityExternal> Externals { get; set; } = []; public virtual ICollection<EntityExternal> Externals { get; set; } = [];
-24
View File
@@ -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!;
}
@@ -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<T> WhereLocale(
this IQueryable<T> query, Expression<Func<T, LocaleString>> expression, string searchTerm) where T : class
{
return query.Where(e => DbContext.LocaleSearch(e.Name.ToJson(), searchTerm));
}
public static IQueryable<Entity> WhereLocale(
this IQueryable<Entity> query, ICollection<string> searchTerms)
{
return query.Where(e => EF.Functions.LocaleSearchArray(e.Name.ToJson(), searchTerms));
}*/
}
+10
View File
@@ -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<string, string> item) : this(LocaleExtension.GetTypeByString(item.Key), item.Value) { }
}
+148
View File
@@ -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<LocaleItem>
{
private readonly List<LocaleItem> _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<LocaleType> 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<LocaleItem> 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;
}
}
+24
View File
@@ -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,
}
-21
View File
@@ -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,
}
+1 -1
View File
@@ -16,7 +16,7 @@ public partial class Ancestry : IEntity
public Entity Entity { get; set; } = null!; public Entity Entity { get; set; } = null!;
[Column(Order = 2)] [Column(Order = 2)]
public Size[] Size { get; set; } public Size[] Size { get; set; } = [];
[Column(Order = 3)] [Column(Order = 3)]
public int Hp { get; set; } public int Hp { get; set; }
+2 -3
View File
@@ -6,6 +6,7 @@ using Pf2eModel.Model.Base.Keywords;
using Pf2eModel.Model.Base.Source; using Pf2eModel.Model.Base.Source;
using Pf2eModel.Model.Base.Targets; using Pf2eModel.Model.Base.Targets;
using Pf2eModel.Model.Common; using Pf2eModel.Model.Common;
using Pf2eModel.Model.Common.I18n;
using Pf2eModel.Model.Enum; using Pf2eModel.Model.Enum;
using Pf2eModel.Model.Structure; using Pf2eModel.Model.Structure;
using Pf2eModel.Model.Structure.AncestryRelation; using Pf2eModel.Model.Structure.AncestryRelation;
@@ -72,8 +73,6 @@ public partial class Pf2eDbContext : DbContext
public virtual DbSet<Entity> Entities { get; set; } public virtual DbSet<Entity> Entities { get; set; }
public virtual DbSet<EntityAlias> EntityAliases { get; set; }
public virtual DbSet<EntityExternal> EntityExternals { get; set; } public virtual DbSet<EntityExternal> EntityExternals { get; set; }
public virtual DbSet<EntitySource> EntitySources { get; set; } public virtual DbSet<EntitySource> EntitySources { get; set; }
@@ -125,7 +124,7 @@ public partial class Pf2eDbContext : DbContext
builder builder
.MapEnum<EntityType>() .MapEnum<EntityType>()
.MapEnum<EntityRarity>() .MapEnum<EntityRarity>()
.MapEnum<AliasType>() .MapEnum<LocaleType>()
.MapEnum<TraitType>() .MapEnum<TraitType>()
.MapEnum<ActionCategory>() .MapEnum<ActionCategory>()
.MapEnum<Size>() .MapEnum<Size>()
+59 -15
View File
@@ -1,11 +1,13 @@
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders; using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
using Pf2eModel.Model; using Pf2eModel.Model;
using Pf2eModel.Model.Base; using Pf2eModel.Model.Base;
using Pf2eModel.Model.Base.Keywords; using Pf2eModel.Model.Base.Keywords;
using Pf2eModel.Model.Base.Source; using Pf2eModel.Model.Base.Source;
using Pf2eModel.Model.Base.Targets; using Pf2eModel.Model.Base.Targets;
using Pf2eModel.Model.Common; using Pf2eModel.Model.Common;
using Pf2eModel.Model.Common.I18n;
using Pf2eModel.Model.Enum; using Pf2eModel.Model.Enum;
using Pf2eModel.Model.Structure; using Pf2eModel.Model.Structure;
using Pf2eModel.Model.Structure.AncestryRelation; using Pf2eModel.Model.Structure.AncestryRelation;
@@ -15,6 +17,8 @@ using Pf2eModel.Model.Structure.ClassRelation;
using Pf2eModel.Model.Structure.DeityRelation; using Pf2eModel.Model.Structure.DeityRelation;
using Pf2eModel.Model.Structure.FeatRelation; using Pf2eModel.Model.Structure.FeatRelation;
using Pf2eModel.Model.Structure.SpellRelation; using Pf2eModel.Model.Structure.SpellRelation;
using System.Linq.Expressions;
using System.Reflection;
namespace Pf2eModel; namespace Pf2eModel;
@@ -30,6 +34,32 @@ public partial class Pf2eDbContext
DefineBase(modelBuilder); DefineBase(modelBuilder);
DefineCommon(modelBuilder); DefineCommon(modelBuilder);
DefineStructure(modelBuilder); DefineStructure(modelBuilder);
DefineMethods(modelBuilder);
}
private void BindEntity<T>(EntityTypeBuilder<T> entity) where T : class, IEntity
{
entity.HasOne(entity => entity.Entity)
.WithOne()
.HasForeignKey<T>(entity => entity.Id)
.IsRequired();
}
private void BindLocaleString<T>(EntityTypeBuilder<T> entity, Expression<Func<T, LocaleString>> 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) private void DefineEnums(ModelBuilder modelBuilder)
@@ -37,7 +67,7 @@ public partial class Pf2eDbContext
modelBuilder modelBuilder
.HasPostgresEnum<EntityType>() .HasPostgresEnum<EntityType>()
.HasPostgresEnum<EntityRarity>() .HasPostgresEnum<EntityRarity>()
.HasPostgresEnum<AliasType>() .HasPostgresEnum<LocaleType>()
.HasPostgresEnum<TraitType>() .HasPostgresEnum<TraitType>()
.HasPostgresEnum<ActionCategory>() .HasPostgresEnum<ActionCategory>()
.HasPostgresEnum<Size>() .HasPostgresEnum<Size>()
@@ -62,15 +92,12 @@ public partial class Pf2eDbContext
{ {
modelBuilder.Entity<Entity>(entity => modelBuilder.Entity<Entity>(entity =>
{ {
BindLocaleString(entity, e => e.Name);
entity.HasMany(e => e.Traits).WithMany(t => t.Entities); entity.HasMany(e => e.Traits).WithMany(t => t.Entities);
entity.HasMany(e => e.Keywords).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 // EntitySource
entity.HasMany(e => e.Sources).WithOne(e => e.Entity) entity.HasMany(e => e.Sources).WithOne(e => e.Entity)
.HasForeignKey(e => e.EntityId) .HasForeignKey(e => e.EntityId)
@@ -81,7 +108,6 @@ public partial class Pf2eDbContext
.HasForeignKey(e => e.EntityId) .HasForeignKey(e => e.EntityId)
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade);
}); });
modelBuilder.Entity<EntityAlias>(entity => { });
// Source // Source
modelBuilder.Entity<Source>(entity => modelBuilder.Entity<Source>(entity =>
@@ -124,14 +150,6 @@ public partial class Pf2eDbContext
modelBuilder.Entity<Training>(entity => { }); modelBuilder.Entity<Training>(entity => { });
} }
private void BindEntity<T>(EntityTypeBuilder<T> entity) where T : class, IEntity
{
entity.HasOne(entity => entity.Entity)
.WithOne()
.HasForeignKey<T>(entity => entity.Id)
.IsRequired();
}
private void DefineStructure(ModelBuilder modelBuilder) private void DefineStructure(ModelBuilder modelBuilder)
{ {
// Action // Action
@@ -341,4 +359,30 @@ public partial class Pf2eDbContext
BindEntity(entity); 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<string>)])!;
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<string> search_values)
=> throw new InvalidOperationException("Only for LINQ translation");
} }
+1
View File
@@ -22,6 +22,7 @@
<!--<PrivateAssets>all</PrivateAssets>--> <!--<PrivateAssets>all</PrivateAssets>-->
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.4" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Npgsql" Version="9.0.3" /> <PackageReference Include="Npgsql" Version="9.0.3" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" /> <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
+3 -2
View File
@@ -5,6 +5,7 @@ using Pf2eModel;
using Pf2eModel.Model; using Pf2eModel.Model;
using Pf2eModel.Model.Base; using Pf2eModel.Model.Base;
using Pf2eModel.Model.Base.Source; using Pf2eModel.Model.Base.Source;
using Pf2eModel.Model.Common.I18n;
using Pf2eModel.Model.Structure; using Pf2eModel.Model.Structure;
using System.ComponentModel; using System.ComponentModel;
@@ -70,11 +71,11 @@ internal class EntityConverter : JsonConverter
private void ProcessTrait(Dictionary<string, object?> dictionary, Trait trait) private void ProcessTrait(Dictionary<string, object?> 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("Entity");
dictionary.Remove("Entities"); dictionary.Remove("Entities");
dictionary.Add("Name", name); dictionary.Add("Name", name.ToString());
} }
private void ProcessEntity(Dictionary<string, object?> dictionary, Entity entity) private void ProcessEntity(Dictionary<string, object?> dictionary, Entity entity)
+1 -1
View File
@@ -296,7 +296,7 @@ public partial class MainViewForm : Form
private class EntityGridRow(Entity e) private class EntityGridRow(Entity e)
{ {
public int Id { get; set; } = e.Id; 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; public EntityRarity Rarity { get; set; } = e.Rarity;
private readonly Entity _e = e; private readonly Entity _e = e;