Отчистка кода

This commit is contained in:
2025-04-24 17:58:58 +04:00
parent 31ad99ade5
commit cc7e97b6ff
20 changed files with 1972 additions and 1562 deletions
@@ -1,35 +1,42 @@
using Pf2eModel.Model.Enum; using Pf2eModel.Model.Enum;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder;
public static class AonprdEnumResolver public static class AonprdEnumResolver
{ {
private static readonly ICollection<Solver> Solvers = [ private static readonly ICollection<Solver> Solvers =
new Solver<SourceCategory>(new() [
{ new Solver<SourceCategory>(
{ "Rulebooks", SourceCategory.Rulebooks }, new()
{ "Adventure Paths", SourceCategory.AdventurePaths }, {
{ "Lost Omens", SourceCategory.LostOmens }, { "Rulebooks", SourceCategory.Rulebooks },
{ "Adventures", SourceCategory.Adventures }, { "Adventure Paths", SourceCategory.AdventurePaths },
{ "Comics", SourceCategory.Comics }, { "Lost Omens", SourceCategory.LostOmens },
{ "Blog Posts", SourceCategory.BlogPosts }, { "Adventures", SourceCategory.Adventures },
{ "Society", SourceCategory.Society }, { "Comics", SourceCategory.Comics },
}), { "Blog Posts", SourceCategory.BlogPosts },
{ "Society", SourceCategory.Society },
new Solver<AncestryVision>(new() }
{ ),
{ "Low-Light Vision", AncestryVision.LowLightVision }, new Solver<AncestryVision>(
{ "Darkvision", AncestryVision.DarkVision }, new()
}), {
{ "Low-Light Vision", AncestryVision.LowLightVision },
new Solver<AttributeValues>(new() { "Darkvision", AncestryVision.DarkVision },
{ }
{ "Consitution", AttributeValues.Constitution }, ),
{ "Other", AttributeValues.Free }, new Solver<AttributeValues>(
{ "other", AttributeValues.Free }, new()
}) {
{ "Consitution", AttributeValues.Constitution },
{ "Other", AttributeValues.Free },
{ "other", AttributeValues.Free },
}
),
]; ];
public static T Resolve<T>(string value) where T : Enum public static T Resolve<T>(string value)
where T : Enum
{ {
Type type = typeof(T); Type type = typeof(T);
@@ -40,9 +47,11 @@ public static class AonprdEnumResolver
throw new EnumResolverEnumUnknownException(type); throw new EnumResolverEnumUnknownException(type);
} }
Solver<T> tSolver = (Solver<T>) solver; Solver<T> tSolver = (Solver<T>)solver;
return tSolver.Data.TryGetValue(value, out T? data) ? data : throw new EnumResolverValueUnknownException(type, value); return tSolver.Data.TryGetValue(value, out T? data)
? data
: throw new EnumResolverValueUnknownException(type, value);
} }
private abstract class Solver private abstract class Solver
@@ -50,14 +59,16 @@ public static class AonprdEnumResolver
public abstract Type Type { get; } public abstract Type Type { get; }
} }
private class Solver<T>(Dictionary<string, T> data) : Solver where T : Enum private class Solver<T>(Dictionary<string, T> data) : Solver
where T : Enum
{ {
public override Type Type { get; } = typeof(T); public override Type Type { get; } = typeof(T);
public Dictionary<string, T> Data { get; } = data; public Dictionary<string, T> Data { get; } = data;
} }
public class EnumResolverEnumUnknownException(Type t) : Exception($"Can't resolve type '{t.FullName}': Solver not defined"); public class EnumResolverEnumUnknownException(Type t)
: Exception($"Can't resolve type '{t.FullName}': Solver not defined");
public class EnumResolverValueUnknownException(Type t, string value) : Exception($"Can't resolve type '{t.FullName}': Value '{value}' is not matched");
public class EnumResolverValueUnknownException(Type t, string value)
: Exception($"Can't resolve type '{t.FullName}': Value '{value}' is not matched");
} }
@@ -1,4 +1,6 @@
using LinqToDB.Reflection; using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
@@ -10,10 +12,6 @@ using Pf2eModel.Model.Base.Targets;
using Pf2eModel.Model.Common.I18n; 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.Text;
using System.Text.RegularExpressions;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors;
@@ -32,7 +30,7 @@ internal abstract partial class AbstractProcessor(AonprdProcessorInput input)
public abstract IEntity Object { get; } public abstract IEntity Object { get; }
protected abstract void Add(); protected abstract void Add();
public virtual void Process() public virtual void Process()
{ {
if (Input.GetOptional<JArray>("remaster_id") is not null) if (Input.GetOptional<JArray>("remaster_id") is not null)
@@ -57,9 +55,11 @@ internal abstract partial class AbstractProcessor(AonprdProcessorInput input)
Context.SaveChanges(); Context.SaveChanges();
} }
protected string AddedDescription(string path) => $"\r\n\r\n========== [{path}] ==========\r\n\r\n{Input.GetOptional<string>(path) ?? ""}"; protected string AddedDescription(string path) =>
$"\r\n\r\n========== [{path}] ==========\r\n\r\n{Input.GetOptional<string>(path) ?? ""}";
protected string AddedDescription(string[] paths) => string.Join("", paths.Select(AddedDescription)); protected string AddedDescription(string[] paths) =>
string.Join("", paths.Select(AddedDescription));
protected virtual string GetDescription() => Input.GetRequired<string>("markdown"); protected virtual string GetDescription() => Input.GetRequired<string>("markdown");
@@ -130,7 +130,7 @@ internal abstract partial class AbstractProcessor(AonprdProcessorInput input)
Entity = Entity, Entity = Entity,
Source = source, Source = source,
Suffix = ((match?.Captures.Count > 0) ? match?.Captures[0]?.Value : "") ?? "", Suffix = ((match?.Captures.Count > 0) ? match?.Captures[0]?.Value : "") ?? "",
IsRemaster = Input.GetOptional<JArray>("legacy_id") is not null IsRemaster = Input.GetOptional<JArray>("legacy_id") is not null,
}; };
Context.EntitySources.Add(entitySource); Context.EntitySources.Add(entitySource);
@@ -140,7 +140,9 @@ internal abstract partial class AbstractProcessor(AonprdProcessorInput input)
{ {
if (AonprdTarget is null) if (AonprdTarget is null)
{ {
AonprdTarget = Context.ExternalTargets.FirstOrDefault(t => t.Name == AonprdImporter.SourceName); AonprdTarget = Context.ExternalTargets.FirstOrDefault(t =>
t.Name == AonprdImporter.SourceName
);
AonprdTarget ??= new() AonprdTarget ??= new()
{ {
@@ -155,7 +157,7 @@ internal abstract partial class AbstractProcessor(AonprdProcessorInput input)
Entity = Entity, Entity = Entity,
Target = AonprdTarget, Target = AonprdTarget,
Suffix = Input.GetRequired<string>("url"), Suffix = Input.GetRequired<string>("url"),
Md5 = Md5(Input.Input.ToString()) Md5 = Md5(Input.Input.ToString()),
}; };
Context.EntityExternals.Add(external); Context.EntityExternals.Add(external);
} }
@@ -170,13 +172,13 @@ 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
.Select(t => t.Entity) .Traits.Select(t => t.Entity)
.Where(e => Pf2eDbContext.LocaleSearchArray(e.Name, names)) .Where(e => Pf2eDbContext.LocaleSearchArray(e.Name, names))
.ToList(); .ToList();
List<Trait> traits = Context.Traits List<Trait> traits = Context
.Include(t => t.Entity) .Traits.Include(t => t.Entity)
.Where(t => traitEntities.Contains(t.Entity)) .Where(t => traitEntities.Contains(t.Entity))
.ToList(); .ToList();
@@ -238,7 +240,9 @@ internal abstract partial class AbstractProcessor(AonprdProcessorInput input)
[GeneratedRegex(@"^\[.*?\]\(.*?\)\s+(.*)$")] [GeneratedRegex(@"^\[.*?\]\(.*?\)\s+(.*)$")]
private static partial Regex SourceSuffixRegex(); private static partial Regex SourceSuffixRegex();
public class LegacyItemException(string name) : Exception($"Item '{name}' is linked with newer version, skipping.") { } public class LegacyItemException(string name)
: Exception($"Item '{name}' is linked with newer version, skipping.") { }
public class SkipItemException(string name, string reason) : Exception($"Item '{name}' is skipped, reason: {reason}.") { } public class SkipItemException(string name, string reason)
: Exception($"Item '{name}' is skipped, reason: {reason}.") { }
} }
@@ -1,6 +1,4 @@
using Newtonsoft.Json.Linq; using Pf2eModel.Model.Enum;
using Pf2eModel.Model;
using Pf2eModel.Model.Enum;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types;
@@ -17,25 +15,30 @@ internal class ActionProcessor(AonprdProcessorInput input) : AbstractProcessor(i
{ {
base.Process(); base.Process();
AonprdProcessorInput.ActionStruct action = Input.ResolveActionString(Input.GetOptional<string>("actions") ?? ""); AonprdProcessorInput.ActionStruct action = Input.ResolveActionString(
Input.GetOptional<string>("actions") ?? ""
);
Object.Actions = action.Actions; Object.Actions = action.Actions;
Object.MaxActions = action.MaxActions; Object.MaxActions = action.MaxActions;
Object.IsFreeAction = action.IsFreeAction; Object.IsFreeAction = action.IsFreeAction;
Object.IsPassive = action.IsPassive; Object.IsPassive = action.IsPassive;
Object.IsReaction = action.IsReaction; Object.IsReaction = action.IsReaction;
; ;
Object.Extra = GenerateExtra("school"); Object.Extra = GenerateExtra("school");
} }
protected override string GetDescription() protected override string GetDescription() =>
=> base.GetDescription() + AddedDescription([ base.GetDescription()
"cost_markdown", + AddedDescription(
"trait_markdown", [
"source_markdown", "cost_markdown",
"search_markdown", "trait_markdown",
"requirement_markdown", "source_markdown",
"trigger_markdown", "search_markdown",
"summary_markdown", "requirement_markdown",
]); "trigger_markdown",
"summary_markdown",
]
);
} }
@@ -1,10 +1,8 @@
using LinqToDB; using LinqToDB;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using Pf2eModel.Model;
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;
using System.Text.RegularExpressions;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types;
@@ -28,8 +26,11 @@ internal class AncestryProcessor(AonprdProcessorInput input) : AbstractProcessor
base.Process(); base.Process();
Object.Size = Input.GetRequired<JArray>("size").Select(t => Input.Enum<Size>(t.ToString())).ToArray(); Object.Size = Input
.GetRequired<JArray>("size")
.Select(t => Input.Enum<Size>(t.ToString()))
.ToArray();
string hp = Input.GetRequired<string>("hp_raw"); string hp = Input.GetRequired<string>("hp_raw");
Object.Hp = Convert.ToInt32((hp != "") ? hp : "0"); Object.Hp = Convert.ToInt32((hp != "") ? hp : "0");
@@ -46,7 +47,8 @@ internal class AncestryProcessor(AonprdProcessorInput input) : AbstractProcessor
protected void AddBoosts(string path, bool isBoost) protected void AddBoosts(string path, bool isBoost)
{ {
List<string> boosts = Input.GetOptional<JArray>(path)?.ToArray().Select(t => t.ToString()).ToList() ?? []; List<string> boosts =
Input.GetOptional<JArray>(path)?.ToArray().Select(t => t.ToString()).ToList() ?? [];
if (boosts.Contains("Two free ability boosts")) if (boosts.Contains("Two free ability boosts"))
{ {
@@ -67,5 +69,4 @@ internal class AncestryProcessor(AonprdProcessorInput input) : AbstractProcessor
Context.AncestryBoosts.Add(ancestryBoost); Context.AncestryBoosts.Add(ancestryBoost);
} }
} }
}
}
@@ -1,10 +1,5 @@
using Pf2eModel.Model.Enum; using Pf2eModel.Model.Enum;
using Pf2eModel.Model.Structure; using Pf2eModel.Model.Structure;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types;
@@ -22,13 +17,9 @@ internal class ArchetypeProcessor(AonprdProcessorInput input) : AbstractProcesso
base.Process(); base.Process();
Object.Level = Input.GetOptional<int>("level"); Object.Level = Input.GetOptional<int>("level");
} }
protected override string GetDescription() protected override string GetDescription() =>
=> base.GetDescription() + AddedDescription([ base.GetDescription()
"prerequisite_markdown", + AddedDescription(["prerequisite_markdown", "requirement_markdown", "summary_markdown"]);
"requirement_markdown",
"summary_markdown"
]);
} }
@@ -1,13 +1,7 @@
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using Pf2eModel.Model;
using Pf2eModel.Model.Enum; using Pf2eModel.Model.Enum;
using Pf2eModel.Model.Structure; using Pf2eModel.Model.Structure;
using Pf2eModel.Model.Structure.BackgroundRelation; using Pf2eModel.Model.Structure.BackgroundRelation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types;
@@ -29,22 +23,17 @@ internal class BackgroundProcessor(AonprdProcessorInput input) : AbstractProcess
BackgroundBoost boost = new() BackgroundBoost boost = new()
{ {
Background = Object, Background = Object,
Attribute = Input.Enum<AttributeValues>(token.ToString()) Attribute = Input.Enum<AttributeValues>(token.ToString()),
}; };
Object.BackgroundBoosts.Add(boost); Object.BackgroundBoosts.Add(boost);
Context.BackgroundBoosts.Add(boost); Context.BackgroundBoosts.Add(boost);
} }
} }
protected override string GetDescription() =>
protected override string GetDescription() base.GetDescription()
=> base.GetDescription() + AddedDescription([ + AddedDescription(
"feat_markdown", ["feat_markdown", "summary_markdown", "skill_markdown", "prerequisite_markdown"]
"summary_markdown", );
"skill_markdown",
"prerequisite_markdown",
]);
} }
@@ -1,18 +1,11 @@
using LinqToDB; using System.Text.RegularExpressions;
using LinqToDB;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using Pf2eModel.Model;
using Pf2eModel.Model.Common; using Pf2eModel.Model.Common;
using Pf2eModel.Model.Enum; using Pf2eModel.Model.Enum;
using Pf2eModel.Model.Structure; using Pf2eModel.Model.Structure;
using Pf2eModel.Model.Structure.ClassRelation; using Pf2eModel.Model.Structure.ClassRelation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types;
@@ -28,10 +21,11 @@ internal class ClassProcessor(AonprdProcessorInput input) : AbstractProcessor(in
public override void Process() public override void Process()
{ {
base.Process(); base.Process();
Object.Hp = Convert.ToInt32(Input.GetRequired<string>("hp_raw")); Object.Hp = Convert.ToInt32(Input.GetRequired<string>("hp_raw"));
AttributeValues[] attributes = Input.GetRequired<JArray>("attribute") AttributeValues[] attributes = Input
.GetRequired<JArray>("attribute")
.Select(token => Input.Enum<AttributeValues>(token.ToString())) .Select(token => Input.Enum<AttributeValues>(token.ToString()))
.ToArray(); .ToArray();
@@ -39,18 +33,24 @@ internal class ClassProcessor(AonprdProcessorInput input) : AbstractProcessor(in
{ {
ClassKeyAbility classKeyAbility = new ClassKeyAbility() ClassKeyAbility classKeyAbility = new ClassKeyAbility()
{ {
Class = Object, Class = Object,
Value = attribute Value = attribute,
}; };
Context.ClassKeyAbilities.Add(classKeyAbility); Context.ClassKeyAbilities.Add(classKeyAbility);
Object.ClassKeyAbilities.Add(classKeyAbility); Object.ClassKeyAbilities.Add(classKeyAbility);
} }
ClassTraining[] classTrainings = ClassTraining[] classTrainings =
[ [
.. ProcessTraining(Input.GetRequired<JArray>("attack_proficiency"), TrainingType.Weapon), .. ProcessTraining(
.. ProcessTraining(Input.GetRequired<JArray>("defense_proficiency"), TrainingType.Armor), Input.GetRequired<JArray>("attack_proficiency"),
TrainingType.Weapon
),
.. ProcessTraining(
Input.GetRequired<JArray>("defense_proficiency"),
TrainingType.Armor
),
.. GetSkillTrainings(), .. GetSkillTrainings(),
.. GetSaveTrainings(), .. GetSaveTrainings(),
]; ];
@@ -84,19 +84,22 @@ internal class ClassProcessor(AonprdProcessorInput input) : AbstractProcessor(in
protected ICollection<ClassTraining> GetSkillTrainings() protected ICollection<ClassTraining> GetSkillTrainings()
{ {
string[] skillNames = Input.GetRequired<JArray>("skill_proficiency") string[] skillNames = Input
.GetRequired<JArray>("skill_proficiency")
.Select(token => token.ToString()) .Select(token => token.ToString())
.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(); // Training[] skills = Context.Trainings.Where(t => Pf2eDbContext.LocaleSearchArray(t.Name, skillNames)).ToArray();
return skills.Select(skill => new ClassTraining() return skills
{ .Select(skill => new ClassTraining()
Class = Object, {
Training = skill, Class = Object,
Stage = TrainingStage.Trained, Training = skill,
}).ToArray(); Stage = TrainingStage.Trained,
})
.ToArray();
} }
protected ICollection<ClassTraining> ProcessTraining(JArray array, TrainingType type) protected ICollection<ClassTraining> ProcessTraining(JArray array, TrainingType type)
@@ -119,16 +122,13 @@ internal class ClassProcessor(AonprdProcessorInput input) : AbstractProcessor(in
foreach (string part in parts) foreach (string part in parts)
{ {
Training? training = Context.Trainings Training? training = Context.Trainings.FirstOrDefault(t =>
.FirstOrDefault(t => t.Name == part && t.Type == type); t.Name == part && t.Type == type
);
if (training == null) if (training == null)
{ {
training = new Training() training = new Training() { Name = part, Type = type };
{
Name = part,
Type = type,
};
Context.Trainings.Add(training); Context.Trainings.Add(training);
} }
@@ -137,7 +137,7 @@ internal class ClassProcessor(AonprdProcessorInput input) : AbstractProcessor(in
{ {
Class = Object, Class = Object,
Training = training, Training = training,
Stage = stage Stage = stage,
}; };
trainings.Add(classTraining); trainings.Add(classTraining);
@@ -149,27 +149,20 @@ internal class ClassProcessor(AonprdProcessorInput input) : AbstractProcessor(in
protected string[] GetTrainingString(string value) protected string[] GetTrainingString(string value)
{ {
if (value.StartsWith("favored weapon of your deity")) return value.StartsWith("favored weapon of your deity") ? ["Favored weapon of your deity"]
{ : value == "simple and martial weapons" ? ["Simple weapon", "Martial weapon"]
return ["Favored weapon of your deity"]; : Regex
} .Split(value, @"\s*(?:,|and)\s*")
.Where(s => s != "")
if (value == "simple and martial weapons") .Select(s => string.Concat(s[0].ToString().ToUpper(), s.AsSpan(1)))
{ .ToArray();
return ["Simple weapon", "Martial weapon"]; ;
}
return Regex.Split(value, @"\s*(?:,|and)\s*")
.Where(s => s != "")
.Select(s => string.Concat(s[0].ToString().ToUpper(), s.AsSpan(1)))
.ToArray();
;
} }
protected ICollection<ClassTraining> GetSaveTrainings() protected ICollection<ClassTraining> GetSaveTrainings()
{ {
Dictionary<string, Training> result = Context.Trainings Dictionary<string, Training> result = Context
.Where(t => t.Type == TrainingType.Save) .Trainings.Where(t => t.Type == TrainingType.Save)
.ToDictionary(t => t.Name); .ToDictionary(t => t.Name);
foreach (string name in new[] { "Will", "Fortitude", "Reflex", "Perception" }) foreach (string name in new[] { "Will", "Fortitude", "Reflex", "Perception" })
@@ -179,23 +172,29 @@ internal class ClassProcessor(AonprdProcessorInput input) : AbstractProcessor(in
continue; continue;
} }
Training training = new() { Name = name, Type = (name == "Perception") ? TrainingType.Perception : TrainingType.Save }; Training training = new()
{
Name = name,
Type = (name == "Perception") ? TrainingType.Perception : TrainingType.Save,
};
result.Add(name, training); result.Add(name, training);
Context.Trainings.Add(training); Context.Trainings.Add(training);
} }
return result.Select(pair => return result
{ .Select(pair =>
ClassTraining classTraining = new()
{ {
Class = Object, ClassTraining classTraining = new()
Training = pair.Value, {
Stage = Input.EnumRequired<TrainingStage>($"{pair.Key.ToLower()}_proficiency"), Class = Object,
}; Training = pair.Value,
Stage = Input.EnumRequired<TrainingStage>($"{pair.Key.ToLower()}_proficiency"),
};
return classTraining; return classTraining;
}).ToList(); })
.ToList();
} }
protected void ProcessDependecies() protected void ProcessDependecies()
@@ -20,11 +20,13 @@ internal class FeatProcessor(AonprdProcessorInput input) : AbstractProcessor(inp
Object.Level = Input.GetRequired<int>("level"); Object.Level = Input.GetRequired<int>("level");
Object.Category = (Entity.Traits.Any(t => t.Type == TraitType.Class)) Object.Category = Entity.Traits.Any(t => t.Type == TraitType.Class)
? ActionCategory.ClassFeat ? ActionCategory.ClassFeat
: ActionCategory.Unknown; : ActionCategory.Unknown;
AonprdProcessorInput.ActionStruct action = Input.ResolveActionString(Input.GetOptional<string>("actions") ?? ""); AonprdProcessorInput.ActionStruct action = Input.ResolveActionString(
Input.GetOptional<string>("actions") ?? ""
);
Object.Actions = action.Actions; Object.Actions = action.Actions;
Object.MaxActions = action.MaxActions; Object.MaxActions = action.MaxActions;
Object.IsFreeAction = action.IsFreeAction; Object.IsFreeAction = action.IsFreeAction;
@@ -39,12 +41,13 @@ internal class FeatProcessor(AonprdProcessorInput input) : AbstractProcessor(inp
"heighten_level", "heighten_level",
"school", "school",
"frequency" "frequency"
); );
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 Archetype[] archetypes = Context
.Where(a => Pf2eDbContext.LocaleSearchArray(a.Entity.Name, archetypeStrings)) .Archetypes.Where(a => Pf2eDbContext.LocaleSearchArray(a.Entity.Name, archetypeStrings))
// .Where(a => archetypeStrings.Contains(a.Entity.Name.ToString())) // .Where(a => archetypeStrings.Contains(a.Entity.Name.ToString()))
.ToArray(); .ToArray();
@@ -53,7 +56,7 @@ internal class FeatProcessor(AonprdProcessorInput input) : AbstractProcessor(inp
Object.Category = ActionCategory.Archetype; Object.Category = ActionCategory.Archetype;
} }
foreach (var archetype in archetypes) foreach (Archetype? archetype in archetypes)
{ {
Object.Archetypes.Add(archetype); Object.Archetypes.Add(archetype);
@@ -64,13 +67,16 @@ internal class FeatProcessor(AonprdProcessorInput input) : AbstractProcessor(inp
} }
} }
protected override string GetDescription() protected override string GetDescription() =>
=> base.GetDescription() + AddedDescription([ base.GetDescription()
"cost_markdown", + AddedDescription(
"feat_markdown", [
"summary_markdown", "cost_markdown",
"prerequisite_markdown", "feat_markdown",
"requirement_markdown", "summary_markdown",
"trigger_markdown" "prerequisite_markdown",
]); "requirement_markdown",
"trigger_markdown",
]
);
} }
@@ -1,12 +1,6 @@
using Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder; using Pf2eModel.Model;
using Pf2eModel.Model;
using Pf2eModel.Model.Base.Source; using Pf2eModel.Model.Base.Source;
using Pf2eModel.Model.Enum; using Pf2eModel.Model.Enum;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types;
@@ -15,7 +9,7 @@ internal class SourceProcessor(AonprdProcessorInput input) : AbstractProcessor(i
{ {
public override EntityType Type => EntityType.Unknown; public override EntityType Type => EntityType.Unknown;
public override IEntity Object => null; public override IEntity? Object => null;
protected override void Add() { } protected override void Add() { }
@@ -1,12 +1,6 @@
using Pf2eModel.Model.Base.Source; using Pf2eModel.Model;
using Pf2eModel.Model.Enum;
using Pf2eModel.Model;
using Pf2eModel.Model.Common; using Pf2eModel.Model.Common;
using System; using Pf2eModel.Model.Enum;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types;
@@ -15,7 +9,7 @@ internal class TrainingProcessor(AonprdProcessorInput input) : AbstractProcessor
{ {
public override EntityType Type => EntityType.Unknown; public override EntityType Type => EntityType.Unknown;
public override IEntity Object => null; public override IEntity? Object => null;
protected override void Add() { } protected override void Add() { }
+14 -12
View File
@@ -1,8 +1,4 @@
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
@@ -14,7 +10,8 @@ public partial class Function : Migration
/// <inheritdoc /> /// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder) protected override void Up(MigrationBuilder migrationBuilder)
{ {
migrationBuilder.Sql(""" migrationBuilder.Sql(
"""
CREATE OR REPLACE FUNCTION f_truncate_tables() CREATE OR REPLACE FUNCTION f_truncate_tables()
RETURNS void RETURNS void
LANGUAGE plpgsql AS LANGUAGE plpgsql AS
@@ -30,8 +27,10 @@ BEGIN
); );
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
@@ -46,8 +45,10 @@ BEGIN
); );
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
@@ -68,7 +69,8 @@ $func$;
""" """
); );
migrationBuilder.Sql(""" migrationBuilder.Sql(
"""
CREATE OR REPLACE FUNCTION locale_search(data jsonb, search_value text) CREATE OR REPLACE FUNCTION locale_search(data jsonb, search_value text)
RETURNS boolean RETURNS boolean
LANGUAGE plpgsql AS LANGUAGE plpgsql AS
@@ -80,7 +82,8 @@ $func$;
""" """
); );
migrationBuilder.Sql(""" migrationBuilder.Sql(
"""
CREATE OR REPLACE FUNCTION locale_search_array(data jsonb, search_values text[]) CREATE OR REPLACE FUNCTION locale_search_array(data jsonb, search_values text[])
RETURNS boolean RETURNS boolean
LANGUAGE plpgsql AS LANGUAGE plpgsql AS
@@ -95,7 +98,6 @@ END;
$func$; $func$;
""" """
); );
} }
/// <inheritdoc /> /// <inheritdoc />
File diff suppressed because it is too large Load Diff
+11 -8
View File
@@ -1,30 +1,33 @@
using Microsoft.EntityFrameworkCore; using System.ComponentModel;
using Pf2eModel.Model.Base;
using System.ComponentModel;
using System.Linq.Expressions;
using System.Reflection; using System.Reflection;
namespace Pf2eModel.Model.Common.I18n; namespace Pf2eModel.Model.Common.I18n;
public static class LocaleExtension public static class LocaleExtension
{ {
public static string GetTypeString(this LocaleType localeType) public static string GetTypeString(this LocaleType localeType)
{ {
FieldInfo fi = localeType.GetType().GetField(localeType.ToString())!; FieldInfo fi = localeType.GetType().GetField(localeType.ToString())!;
DescriptionAttribute[] attributes = (DescriptionAttribute[]) fi.GetCustomAttributes(typeof(DescriptionAttribute), false); DescriptionAttribute[] attributes = (DescriptionAttribute[])
fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
return (attributes.Length != 0 ? attributes.FirstOrDefault()?.Description : null) ?? localeType.ToString(); return (attributes.Length != 0 ? attributes.FirstOrDefault()?.Description : null)
?? localeType.ToString();
} }
public static LocaleType GetTypeByString(this string localeType) public static LocaleType GetTypeByString(this string localeType)
{ {
foreach (System.Reflection.FieldInfo field in typeof(LocaleType).GetFields()) foreach (System.Reflection.FieldInfo field in typeof(LocaleType).GetFields())
{ {
if (Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) is DescriptionAttribute descriptionAttribute) if (
Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute))
is DescriptionAttribute descriptionAttribute
)
{ {
if (descriptionAttribute.Description == localeType) if (descriptionAttribute.Description == localeType)
{ {
return (LocaleType) field.GetValue(null)!; return (LocaleType)field.GetValue(null)!;
} }
} }
} }
+14 -13
View File
@@ -1,18 +1,12 @@
using Newtonsoft.Json.Linq; using System.Collections;
using System; using Newtonsoft.Json.Linq;
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; namespace Pf2eModel.Model.Common.I18n;
public class LocaleString : ICollection<LocaleItem> public class LocaleString : ICollection<LocaleItem>
{ {
private readonly List<LocaleItem> _items = []; private readonly List<LocaleItem> _items = [];
private void AssureDefault() private void AssureDefault()
{ {
if (_items.Count == 0) if (_items.Count == 0)
@@ -34,16 +28,19 @@ public class LocaleString : ICollection<LocaleItem>
private LocaleType _E(string localeString) => LocaleExtension.GetTypeByString(localeString); private LocaleType _E(string localeString) => LocaleExtension.GetTypeByString(localeString);
private LocaleType RealLocale(LocaleType locale) => Contains(locale) ? locale : LocaleType.English; private LocaleType RealLocale(LocaleType locale) =>
Contains(locale) ? locale : LocaleType.English;
private LocaleType RealLocale(string localeString) => RealLocale(_E(localeString)); private LocaleType RealLocale(string localeString) => RealLocale(_E(localeString));
public LocaleItem GetItem(LocaleType locale) => this.First(c => c.Locale == RealLocale(locale)); 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 LocaleItem GetItem(string locale) => this.First(c => c.Locale == RealLocale(locale));
public string this[LocaleType locale] public string this[LocaleType locale]
{ {
get => GetItem(locale).Value; get => GetItem(locale).Value;
set set
{ {
if (Types.Contains(locale)) if (Types.Contains(locale))
{ {
@@ -65,6 +62,7 @@ public class LocaleString : ICollection<LocaleItem>
{ {
this[item.Locale] = item.Value; this[item.Locale] = item.Value;
} }
public void Add(LocaleType locale, string value) public void Add(LocaleType locale, string value)
{ {
this[locale] = value; this[locale] = value;
@@ -80,10 +78,12 @@ public class LocaleString : ICollection<LocaleItem>
{ {
return _items.Contains(item); return _items.Contains(item);
} }
public bool Contains(LocaleType locale) public bool Contains(LocaleType locale)
{ {
return Types.Contains(locale); return Types.Contains(locale);
} }
public bool Contains(string value) public bool Contains(string value)
{ {
return this.Any(item => item.Value == value); return this.Any(item => item.Value == value);
@@ -111,6 +111,7 @@ public class LocaleString : ICollection<LocaleItem>
{ {
return _items.GetEnumerator(); return _items.GetEnumerator();
} }
IEnumerator IEnumerable.GetEnumerator() IEnumerator IEnumerable.GetEnumerator()
{ {
return GetEnumerator(); return GetEnumerator();
@@ -132,17 +133,17 @@ public class LocaleString : ICollection<LocaleItem>
return json.ToString(); return json.ToString();
} }
public static LocaleString Deserialize(string input) public static LocaleString Deserialize(string input)
{ {
JObject json = JObject.Parse(input); JObject json = JObject.Parse(input);
LocaleString result = new(); LocaleString result = new();
foreach (var item in json) foreach (KeyValuePair<string, JToken?> item in json)
{ {
result[item.Key] = item.ToString(); result[item.Key] = item.ToString();
} }
return result; return result;
} }
} }
+3 -8
View File
@@ -1,12 +1,7 @@
using Pf2eModel.Model.Base; using System.ComponentModel.DataAnnotations;
using Pf2eModel.Model.Enum;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using System.Linq; using Pf2eModel.Model.Base;
using System.Text; using Pf2eModel.Model.Enum;
using System.Threading.Tasks;
namespace Pf2eModel.Model.Structure; namespace Pf2eModel.Model.Structure;
@@ -1,9 +1,4 @@
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Pf2eModel.Model.Structure.ArchetypeRelation; namespace Pf2eModel.Model.Structure.ArchetypeRelation;
@@ -11,6 +6,6 @@ namespace Pf2eModel.Model.Structure.ArchetypeRelation;
internal class ArchetypeFeats internal class ArchetypeFeats
{ {
public int ArchetypeId { get; set; } public int ArchetypeId { get; set; }
public int FeatId { get; set; } public int FeatId { get; set; }
} }
+4 -5
View File
@@ -1,12 +1,12 @@
using Pf2eModel.Model.Base; using System.ComponentModel.DataAnnotations;
using Pf2eModel.Model.Enum;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using Pf2eModel.Model.Base;
using Pf2eModel.Model.Enum;
namespace Pf2eModel.Model.Structure; namespace Pf2eModel.Model.Structure;
[EntityOfType(EntityType.Language)] [EntityOfType(EntityType.Language)]
public partial class Language: IEntity public partial class Language : IEntity
{ {
[Key] [Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)] [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
@@ -14,5 +14,4 @@ public partial class Language: IEntity
public int Id { get; set; } public int Id { get; set; }
public Entity Entity { get; set; } = null!; public Entity Entity { get; set; } = null!;
} }
+144 -65
View File
@@ -1,6 +1,7 @@
using Microsoft.EntityFrameworkCore; using System.Linq.Expressions;
using System.Reflection;
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;
@@ -17,8 +18,6 @@ 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;
@@ -37,15 +36,21 @@ public partial class Pf2eDbContext
DefineMethods(modelBuilder); DefineMethods(modelBuilder);
} }
private void BindEntity<T>(EntityTypeBuilder<T> entity) where T : class, IEntity private void BindEntity<T>(EntityTypeBuilder<T> entity)
where T : class, IEntity
{ {
entity.HasOne(entity => entity.Entity) entity
.HasOne(entity => entity.Entity)
.WithOne() .WithOne()
.HasForeignKey<T>(entity => entity.Id) .HasForeignKey<T>(entity => entity.Id)
.IsRequired(); .IsRequired();
} }
private void BindLocaleString<T>(EntityTypeBuilder<T> entity, Expression<Func<T, LocaleString>> func) where T : class private void BindLocaleString<T>(
EntityTypeBuilder<T> entity,
Expression<Func<T, LocaleString>> func
)
where T : class
{ {
/* /*
entity.OwnsMany(func, d => entity.OwnsMany(func, d =>
@@ -54,11 +59,9 @@ public partial class Pf2eDbContext
}); });
*/ */
entity.Property(func) entity
.HasConversion( .Property(func)
v => v.Serialize(), .HasConversion(v => v.Serialize(), v => LocaleString.Deserialize(v))
v => LocaleString.Deserialize(v)
)
.HasColumnType("jsonb"); .HasColumnType("jsonb");
} }
@@ -84,8 +87,7 @@ public partial class Pf2eDbContext
.HasPostgresEnum<SpellDamageKinds>() .HasPostgresEnum<SpellDamageKinds>()
.HasPostgresEnum<AttributeValues>() .HasPostgresEnum<AttributeValues>()
.HasPostgresEnum<TrainingStage>() .HasPostgresEnum<TrainingStage>()
.HasPostgresEnum<SourceCategory>() .HasPostgresEnum<SourceCategory>();
;
} }
private void DefineBase(ModelBuilder modelBuilder) private void DefineBase(ModelBuilder modelBuilder)
@@ -99,12 +101,16 @@ public partial class Pf2eDbContext
entity.HasMany(e => e.Keywords).WithMany(t => t.Entities); entity.HasMany(e => e.Keywords).WithMany(t => t.Entities);
// 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)
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade);
// EntityExternal // EntityExternal
entity.HasMany(e => e.Externals).WithOne(e => e.Entity) entity
.HasMany(e => e.Externals)
.WithOne(e => e.Entity)
.HasForeignKey(e => e.EntityId) .HasForeignKey(e => e.EntityId)
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade);
}); });
@@ -114,7 +120,9 @@ public partial class Pf2eDbContext
{ {
entity.Property(e => e.Id).HasColumnName("id"); entity.Property(e => e.Id).HasColumnName("id");
entity.HasMany(s => s.EntitySources).WithOne(e => e.Source) entity
.HasMany(s => s.EntitySources)
.WithOne(e => e.Source)
.HasForeignKey(e => e.SourceId) .HasForeignKey(e => e.SourceId)
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade);
@@ -125,7 +133,9 @@ public partial class Pf2eDbContext
// External // External
modelBuilder.Entity<ExternalTarget>(entity => modelBuilder.Entity<ExternalTarget>(entity =>
{ {
entity.HasMany(t => t.EntityExternals).WithOne(e => e.Target) entity
.HasMany(t => t.EntityExternals)
.WithOne(e => e.Target)
.HasForeignKey(e => e.TargetId) .HasForeignKey(e => e.TargetId)
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade);
}); });
@@ -134,7 +144,9 @@ public partial class Pf2eDbContext
// Keywords // Keywords
modelBuilder.Entity<Keyword>(entity => modelBuilder.Entity<Keyword>(entity =>
{ {
entity.HasMany(e => e.Aliases).WithOne(e => e.Keyword) entity
.HasMany(e => e.Aliases)
.WithOne(e => e.Keyword)
.HasForeignKey(e => e.KeywordId) .HasForeignKey(e => e.KeywordId)
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade);
}); });
@@ -163,28 +175,38 @@ public partial class Pf2eDbContext
{ {
BindEntity(entity); BindEntity(entity);
entity.HasMany(e => e.AncestryBoosts).WithOne(e => e.Ancestry) entity
.HasMany(e => e.AncestryBoosts)
.WithOne(e => e.Ancestry)
.HasForeignKey(e => e.AncestryId) .HasForeignKey(e => e.AncestryId)
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade);
entity.HasMany(e => e.AncestryItems).WithOne(e => e.Ancestry) entity
.HasMany(e => e.AncestryItems)
.WithOne(e => e.Ancestry)
.HasForeignKey(e => e.AncestryId) .HasForeignKey(e => e.AncestryId)
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade);
entity.HasMany(e => e.AncestryLanguages).WithOne(e => e.Ancestry) entity
.HasMany(e => e.AncestryLanguages)
.WithOne(e => e.Ancestry)
.HasForeignKey(e => e.AncestryId) .HasForeignKey(e => e.AncestryId)
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade);
}); });
modelBuilder.Entity<AncestryBoost>(entity => { }); modelBuilder.Entity<AncestryBoost>(entity => { });
modelBuilder.Entity<AncestryItem>(entity => modelBuilder.Entity<AncestryItem>(entity =>
{ {
entity.HasOne(d => d.Item).WithMany() entity
.HasOne(d => d.Item)
.WithMany()
.HasForeignKey(d => d.ItemId) .HasForeignKey(d => d.ItemId)
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade);
}); });
modelBuilder.Entity<AncestryLanguage>(entity => modelBuilder.Entity<AncestryLanguage>(entity =>
{ {
entity.HasOne(d => d.Language).WithMany() entity
.HasOne(d => d.Language)
.WithMany()
.HasForeignKey(d => d.LanguageId) .HasForeignKey(d => d.LanguageId)
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade);
}); });
@@ -194,11 +216,15 @@ public partial class Pf2eDbContext
{ {
BindEntity(entity); BindEntity(entity);
entity.HasOne(a => a.DedicationFeat).WithOne() entity
.HasOne(a => a.DedicationFeat)
.WithOne()
.HasForeignKey<Archetype>(a => a.DedicationFeatId) .HasForeignKey<Archetype>(a => a.DedicationFeatId)
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade);
entity.HasMany(a => a.Feats).WithMany(f => f.Archetypes) entity
.HasMany(a => a.Feats)
.WithMany(f => f.Archetypes)
.UsingEntity<ArchetypeFeats>( .UsingEntity<ArchetypeFeats>(
l => l.HasOne<Feat>().WithMany().HasForeignKey(af => af.FeatId), l => l.HasOne<Feat>().WithMany().HasForeignKey(af => af.FeatId),
r => r.HasOne<Archetype>().WithMany().HasForeignKey(af => af.ArchetypeId) r => r.HasOne<Archetype>().WithMany().HasForeignKey(af => af.ArchetypeId)
@@ -212,23 +238,31 @@ public partial class Pf2eDbContext
}); });
modelBuilder.Entity<BackgroundBoost>(entity => modelBuilder.Entity<BackgroundBoost>(entity =>
{ {
entity.HasOne(d => d.Background).WithMany(p => p.BackgroundBoosts) entity
.HasOne(d => d.Background)
.WithMany(p => p.BackgroundBoosts)
.HasForeignKey(d => d.BackgroundId) .HasForeignKey(d => d.BackgroundId)
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade);
}); });
modelBuilder.Entity<BackgroundItem>(entity => modelBuilder.Entity<BackgroundItem>(entity =>
{ {
entity.HasOne(d => d.Background).WithMany(p => p.BackgroundItems) entity
.HasOne(d => d.Background)
.WithMany(p => p.BackgroundItems)
.HasForeignKey(d => d.BackgroundId) .HasForeignKey(d => d.BackgroundId)
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade);
entity.HasOne(d => d.Entity).WithMany() entity
.HasOne(d => d.Entity)
.WithMany()
.HasForeignKey(d => d.EntityId) .HasForeignKey(d => d.EntityId)
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade);
}); });
modelBuilder.Entity<BackgroundTraining>(entity => modelBuilder.Entity<BackgroundTraining>(entity =>
{ {
entity.HasOne(d => d.Background).WithMany(p => p.BackgroundSkills) entity
.HasOne(d => d.Background)
.WithMany(p => p.BackgroundSkills)
.HasForeignKey(d => d.BackgroundId) .HasForeignKey(d => d.BackgroundId)
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade);
}); });
@@ -240,27 +274,37 @@ public partial class Pf2eDbContext
}); });
modelBuilder.Entity<ClassKeyAbility>(entity => modelBuilder.Entity<ClassKeyAbility>(entity =>
{ {
entity.HasOne(d => d.Class).WithMany(p => p.ClassKeyAbilities) entity
.HasOne(d => d.Class)
.WithMany(p => p.ClassKeyAbilities)
.HasForeignKey(d => d.ClassId) .HasForeignKey(d => d.ClassId)
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade);
}); });
modelBuilder.Entity<ClassProgression>(entity => modelBuilder.Entity<ClassProgression>(entity =>
{ {
entity.HasOne(d => d.Class).WithMany(p => p.ClassProgressions) entity
.HasOne(d => d.Class)
.WithMany(p => p.ClassProgressions)
.HasForeignKey(d => d.ClassId) .HasForeignKey(d => d.ClassId)
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade);
entity.HasOne(d => d.Entity).WithMany() entity
.HasOne(d => d.Entity)
.WithMany()
.HasForeignKey(d => d.EntityId) .HasForeignKey(d => d.EntityId)
.OnDelete(DeleteBehavior.SetNull); .OnDelete(DeleteBehavior.SetNull);
}); });
modelBuilder.Entity<ClassTraining>(entity => modelBuilder.Entity<ClassTraining>(entity =>
{ {
entity.HasOne(d => d.Class).WithMany(p => p.ClassTrainings) entity
.HasOne(d => d.Class)
.WithMany(p => p.ClassTrainings)
.HasForeignKey(d => d.ClassId) .HasForeignKey(d => d.ClassId)
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade);
entity.HasOne(d => d.Training).WithMany() entity
.HasOne(d => d.Training)
.WithMany()
.HasForeignKey(d => d.TrainingId) .HasForeignKey(d => d.TrainingId)
.OnDelete(DeleteBehavior.SetNull); .OnDelete(DeleteBehavior.SetNull);
}); });
@@ -276,19 +320,27 @@ public partial class Pf2eDbContext
{ {
BindEntity(entity); BindEntity(entity);
entity.HasMany(e => e.DeityAttributes).WithOne(e => e.Deity) entity
.HasMany(e => e.DeityAttributes)
.WithOne(e => e.Deity)
.HasForeignKey(e => e.DeityId) .HasForeignKey(e => e.DeityId)
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade);
entity.HasMany(e => e.DeityDomains).WithOne(e => e.Deity) entity
.HasMany(e => e.DeityDomains)
.WithOne(e => e.Deity)
.HasForeignKey(e => e.DeityId) .HasForeignKey(e => e.DeityId)
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade);
entity.HasMany(e => e.DeitySpells).WithOne(e => e.Deity) entity
.HasMany(e => e.DeitySpells)
.WithOne(e => e.Deity)
.HasForeignKey(e => e.DeityId) .HasForeignKey(e => e.DeityId)
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade);
entity.HasMany(e => e.DeityWeapons).WithOne(e => e.Deity) entity
.HasMany(e => e.DeityWeapons)
.WithOne(e => e.Deity)
.HasForeignKey(e => e.DeityId) .HasForeignKey(e => e.DeityId)
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade);
@@ -298,7 +350,9 @@ public partial class Pf2eDbContext
modelBuilder.Entity<DeityDomain>(entity => { }); modelBuilder.Entity<DeityDomain>(entity => { });
modelBuilder.Entity<DeitySpell>(entity => modelBuilder.Entity<DeitySpell>(entity =>
{ {
entity.HasOne(d => d.Spell).WithMany() entity
.HasOne(d => d.Spell)
.WithMany()
.HasForeignKey(d => d.SpellId) .HasForeignKey(d => d.SpellId)
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade);
}); });
@@ -309,7 +363,9 @@ public partial class Pf2eDbContext
{ {
BindEntity(entity); BindEntity(entity);
entity.HasMany(e => e.FeatPrerequisites).WithOne(e => e.Feat) entity
.HasMany(e => e.FeatPrerequisites)
.WithOne(e => e.Feat)
.HasForeignKey(e => e.FeatId) .HasForeignKey(e => e.FeatId)
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade);
}); });
@@ -320,7 +376,9 @@ public partial class Pf2eDbContext
{ {
BindEntity(entity); BindEntity(entity);
entity.HasOne(d => d.Ancestry).WithMany(p => p.Heritages) entity
.HasOne(d => d.Ancestry)
.WithMany(p => p.Heritages)
.HasForeignKey(d => d.AncestryId) .HasForeignKey(d => d.AncestryId)
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade);
}); });
@@ -330,21 +388,29 @@ public partial class Pf2eDbContext
{ {
BindEntity(entity); BindEntity(entity);
entity.HasOne(e => e.SpellArea).WithOne(e => e.Spell) entity
.HasOne(e => e.SpellArea)
.WithOne(e => e.Spell)
.HasForeignKey<SpellArea>(e => e.SpellId) .HasForeignKey<SpellArea>(e => e.SpellId)
.OnDelete(DeleteBehavior.Cascade) .OnDelete(DeleteBehavior.Cascade)
.IsRequired(false); .IsRequired(false);
entity.HasOne(e => e.SpellRitual).WithOne(e => e.Spell) entity
.HasOne(e => e.SpellRitual)
.WithOne(e => e.Spell)
.HasForeignKey<SpellRitual>(e => e.SpellId) .HasForeignKey<SpellRitual>(e => e.SpellId)
.OnDelete(DeleteBehavior.Cascade) .OnDelete(DeleteBehavior.Cascade)
.IsRequired(false); .IsRequired(false);
entity.HasMany(e => e.SpellDamages).WithOne(e => e.Spell) entity
.HasMany(e => e.SpellDamages)
.WithOne(e => e.Spell)
.HasForeignKey(e => e.SpellId) .HasForeignKey(e => e.SpellId)
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade);
entity.HasMany(e => e.SpellDefenses).WithOne(e => e.Spell) entity
.HasMany(e => e.SpellDefenses)
.WithOne(e => e.Spell)
.HasForeignKey(e => e.SpellId) .HasForeignKey(e => e.SpellId)
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade);
}); });
@@ -362,27 +428,40 @@ public partial class Pf2eDbContext
private void DefineMethods(ModelBuilder modelBuilder) private void DefineMethods(ModelBuilder modelBuilder)
{ {
MethodInfo localeSearch = typeof(Pf2eDbContext).GetMethod(nameof(LocaleSearch), BindingFlags.Static | BindingFlags.Public, [typeof(LocaleString), typeof(string)])!; MethodInfo localeSearch = typeof(Pf2eDbContext).GetMethod(
modelBuilder.HasDbFunction(localeSearch, builder => nameof(LocaleSearch),
{ BindingFlags.Static | BindingFlags.Public,
builder.HasName("locale_search"); [typeof(LocaleString), typeof(string)]
builder.HasParameter("data").HasStoreType("jsonb"); )!;
builder.HasParameter("search_value"); 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>)])!; MethodInfo localeSearchArray = typeof(Pf2eDbContext).GetMethod(
modelBuilder.HasDbFunction(localeSearchArray, builder => nameof(LocaleSearchArray),
{ BindingFlags.Static | BindingFlags.Public,
builder.HasName("locale_search_array"); [typeof(LocaleString), typeof(IEnumerable<string>)]
builder.HasParameter("data").HasStoreType("jsonb"); )!;
builder.HasParameter("search_values"); 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) public static bool LocaleSearch(LocaleString data, string search_value) =>
=> throw new InvalidOperationException("Only for LINQ translation"); throw new InvalidOperationException("Only for LINQ translation");
public static bool LocaleSearchArray(LocaleString data, IEnumerable<string> search_values)
=> throw new InvalidOperationException("Only for LINQ translation");
public static bool LocaleSearchArray(LocaleString data, IEnumerable<string> search_values) =>
throw new InvalidOperationException("Only for LINQ translation");
} }
+15 -8
View File
@@ -1,15 +1,13 @@
using Microsoft.EntityFrameworkCore; using System.ComponentModel;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Pf2eModel; using Pf2eModel;
using Pf2eModel.Model; using Pf2eModel.Model;
using Pf2eModel.Model.Base; using Pf2eModel.Model.Base;
using Pf2eModel.Model.Base.Source;
using Pf2eModel.Model.Common.I18n; using Pf2eModel.Model.Common.I18n;
using Pf2eModel.Model.Structure; using Pf2eModel.Model.Structure;
using System.ComponentModel;
namespace Pf2eViewer; namespace Pf2eViewer;
internal class EntityConverter : JsonConverter internal class EntityConverter : JsonConverter
{ {
private readonly Pf2eDbContext _context = new(); private readonly Pf2eDbContext _context = new();
@@ -71,7 +69,9 @@ internal class EntityConverter : JsonConverter
private void ProcessTrait(Dictionary<string, object?> dictionary, Trait trait) private void ProcessTrait(Dictionary<string, object?> dictionary, Trait trait)
{ {
LocaleString 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");
@@ -82,11 +82,18 @@ internal class EntityConverter : JsonConverter
{ {
dictionary.Remove("Description"); dictionary.Remove("Description");
} }
public override bool CanRead => false; public override bool CanRead => false;
public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) public override object? ReadJson(
JsonReader reader,
Type objectType,
object? existingValue,
JsonSerializer serializer
)
{ {
throw new NotImplementedException("Unnecessary because CanRead is false. The type will skip the converter."); throw new NotImplementedException(
"Unnecessary because CanRead is false. The type will skip the converter."
);
} }
} }
+52 -51
View File
@@ -1,5 +1,4 @@
using LinqToDB.Tools; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Converters; using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
@@ -7,8 +6,8 @@ using Newtonsoft.Json.Serialization;
using Pf2eModel; using Pf2eModel;
using Pf2eModel.Model; using Pf2eModel.Model;
using Pf2eModel.Model.Base; using Pf2eModel.Model.Base;
using Pf2eModel.Model.Common.I18n;
using Pf2eModel.Model.Enum; using Pf2eModel.Model.Enum;
using static LinqToDB.Common.Configuration;
namespace Pf2eViewer; namespace Pf2eViewer;
@@ -49,11 +48,10 @@ public partial class MainViewForm : Form
public int MaxPage => Convert.ToInt32(Math.Ceiling(1.0 * Count / PerPage)); public int MaxPage => Convert.ToInt32(Math.Ceiling(1.0 * Count / PerPage));
private void UpdateTreeView() private void UpdateTreeView()
{ {
EntityTreeView.Nodes.Clear(); EntityTreeView.Nodes.Clear();
EntityType type = (EntityType) TypesListBox.SelectedItem!; EntityType type = (EntityType)TypesListBox.SelectedItem!;
Count = Context.Entities.Count(e => e.Type == type); Count = Context.Entities.Count(e => e.Type == type);
@@ -63,8 +61,8 @@ public partial class MainViewForm : Form
return; return;
} }
EntityGridView.DataSource = Context.Entities EntityGridView.DataSource = Context
.Where(e => e.Type == (EntityType) TypesListBox.SelectedItem) .Entities.Where(e => e.Type == (EntityType)TypesListBox.SelectedItem)
.OrderBy(e => e.Id) .OrderBy(e => e.Id)
.Select(e => new EntityGridRow(e)) .Select(e => new EntityGridRow(e))
.Skip((Page - 1) * PerPage) .Skip((Page - 1) * PerPage)
@@ -90,22 +88,27 @@ public partial class MainViewForm : Form
Page = 1; Page = 1;
} }
private void FirstPageButton_Click(object sender, EventArgs e) private void FirstPageButton_Click(object sender, EventArgs e)
{ {
Page = 1; Page = 1;
} }
private void PrevPageButton_Click(object sender, EventArgs e) private void PrevPageButton_Click(object sender, EventArgs e)
{ {
Page--; Page--;
} }
private void NextPageButton_Click(object sender, EventArgs e) private void NextPageButton_Click(object sender, EventArgs e)
{ {
Page++; Page++;
} }
private void LastPageButton_Click(object sender, EventArgs e) private void LastPageButton_Click(object sender, EventArgs e)
{ {
Page = MaxPage; Page = MaxPage;
} }
private void PageTextBox_KeyPress(object sender, KeyPressEventArgs e) private void PageTextBox_KeyPress(object sender, KeyPressEventArgs e)
{ {
if (!char.IsControl(e.KeyChar) && !char.IsDigit(e.KeyChar)) if (!char.IsControl(e.KeyChar) && !char.IsDigit(e.KeyChar))
@@ -113,14 +116,16 @@ public partial class MainViewForm : Form
e.Handled = true; e.Handled = true;
} }
if (e.KeyChar == (char) Keys.Return) if (e.KeyChar == (char)Keys.Return)
{ {
UpdateTreeView(); UpdateTreeView();
} }
} }
private void EntityGridView_RowStateChanged(
private void EntityGridView_RowStateChanged(object sender, DataGridViewRowStateChangedEventArgs e) object sender,
DataGridViewRowStateChangedEventArgs e
)
{ {
int rowsCount = EntityGridView.SelectedRows.Count; int rowsCount = EntityGridView.SelectedRows.Count;
if (rowsCount is 0 or > 1) if (rowsCount is 0 or > 1)
@@ -130,7 +135,7 @@ public partial class MainViewForm : Form
DataGridViewRow row = EntityGridView.SelectedRows[0]; DataGridViewRow row = EntityGridView.SelectedRows[0];
EntityGridRow data = (EntityGridRow) row.DataBoundItem; EntityGridRow data = (EntityGridRow)row.DataBoundItem;
ViewEntity(data.Id); ViewEntity(data.Id);
} }
@@ -139,18 +144,16 @@ public partial class MainViewForm : Form
{ {
EntityTreeView.Nodes.Clear(); EntityTreeView.Nodes.Clear();
Entity? entity = Context.Entities Entity? entity = Context
.Where(e => e.Id == id && e.Type == (EntityType) TypesListBox.SelectedItem!) .Entities.Where(e => e.Id == id && e.Type == (EntityType)TypesListBox.SelectedItem!)
.Include(e => e.Sources) .Include(e => e.Sources)
.ThenInclude(s => s.Source) .ThenInclude(s => s.Source)
.Include(e => e.Aliases) .Include(e => e.Keywords)
.Include(e => e.Keywords) .Include(e => e.Externals)
.Include(e => e.Externals) .ThenInclude(x => x.Target)
.ThenInclude(x => x.Target) .Include(e => e.Traits)
.Include(e => e.Traits) .ThenInclude(t => t.Entity)
.ThenInclude(t => t.Entity) .FirstOrDefault();
.FirstOrDefault()
;
if (entity == null) if (entity == null)
{ {
@@ -158,7 +161,6 @@ public partial class MainViewForm : Form
return; return;
} }
string json = SerializeEntity(entity); string json = SerializeEntity(entity);
JsonToTreeview(EntityTreeView, json, "Entity"); JsonToTreeview(EntityTreeView, json, "Entity");
@@ -166,13 +168,12 @@ public partial class MainViewForm : Form
DescriptionTextBox.Text = entity.Description.Replace("\\r", "").Replace("\\n", "\\r\\n"); DescriptionTextBox.Text = entity.Description.Replace("\\r", "").Replace("\\n", "\\r\\n");
} }
public string SerializeEntity(Entity entity) public string SerializeEntity(Entity entity)
{ {
JsonSerializerSettings settings = new() JsonSerializerSettings settings = new()
{ {
Converters = [ Converters =
[
new EntityConverter(), new EntityConverter(),
new StringEnumConverter { NamingStrategy = new CamelCaseNamingStrategy() }, new StringEnumConverter { NamingStrategy = new CamelCaseNamingStrategy() },
], ],
@@ -192,51 +193,51 @@ public partial class MainViewForm : Form
switch (entity.Type) switch (entity.Type)
{ {
case EntityType.Ancestry: case EntityType.Ancestry:
return Context.Ancestries return Context
.Include(a => a.AncestryBoosts) .Ancestries.Include(a => a.AncestryBoosts)
.FirstOrDefault(e => e.Id == entity.Id); .FirstOrDefault(e => e.Id == entity.Id);
case EntityType.Archetype: case EntityType.Archetype:
return Context.Archetypes return Context
.Include(a => a.DedicationFeat) .Archetypes.Include(a => a.DedicationFeat)
.ThenInclude(f => f.Entity) .ThenInclude(f => f.Entity)
.Include(a => a.Feats) .Include(a => a.Feats)
.ThenInclude(f => f.Entity) .ThenInclude(f => f.Entity)
.FirstOrDefault(a => a.Id == entity.Id); .FirstOrDefault(a => a.Id == entity.Id);
case EntityType.Background: case EntityType.Background:
return Context.Backgrounds return Context
.Include(b => b.BackgroundBoosts) .Backgrounds.Include(b => b.BackgroundBoosts)
.Include(b => b.BackgroundSkills) .Include(b => b.BackgroundSkills)
.ThenInclude(t => t.Training) .ThenInclude(t => t.Training)
.Include(b => b.BackgroundItems) .Include(b => b.BackgroundItems)
.FirstOrDefault(e => e.Id == entity.Id); .FirstOrDefault(e => e.Id == entity.Id);
case EntityType.Class: case EntityType.Class:
return Context.Classes return Context
.Include(c => c.ClassKeyAbilities) .Classes.Include(c => c.ClassKeyAbilities)
.Include(c => c.ClassTrainings) .Include(c => c.ClassTrainings)
.ThenInclude(t => t.Training) .ThenInclude(t => t.Training)
.Include(c => c.ClassProgressions) .Include(c => c.ClassProgressions)
.FirstOrDefault(c => c.Id == entity.Id); .FirstOrDefault(c => c.Id == entity.Id);
case EntityType.Feat: case EntityType.Feat:
return Context.Feats return Context
.Include(f => f.Archetypes) .Feats.Include(f => f.Archetypes)
.ThenInclude(a => a.Entity) .ThenInclude(a => a.Entity)
.FirstOrDefault(f => f.Id == entity.Id); .FirstOrDefault(f => f.Id == entity.Id);
default: default:
Type type = EntityOfTypeAttribute.Resolve(entity); Type type = EntityOfTypeAttribute.Resolve(entity);
return (IEntity?) Context.Find(type, entity.Id); return (IEntity?)Context.Find(type, entity.Id);
} }
} }
public static void JsonToTreeview(TreeView treeView, string jsonString, string rootName) public static void JsonToTreeview(TreeView treeView, string jsonString, string rootName)
{ {
object json = jsonString.StartsWith('[') object json = jsonString.StartsWith('[')
? JArray.Parse(jsonString) ? JArray.Parse(jsonString)
: JObject.Parse(jsonString); : JObject.Parse(jsonString);
treeView.Nodes.Clear(); treeView.Nodes.Clear();
foreach (TreeNode node in Element2TreeNode(json, rootName).Nodes) foreach (TreeNode node in Element2TreeNode(json, rootName).Nodes)
@@ -253,7 +254,7 @@ public partial class MainViewForm : Form
switch (element.GetType().Name) switch (element.GetType().Name)
{ {
case "JObject": case "JObject":
JObject jObject = (JObject) element; JObject jObject = (JObject)element;
foreach (KeyValuePair<string, JToken> token in jObject) foreach (KeyValuePair<string, JToken> token in jObject)
{ {
subNodes.Add(Element2TreeNode(token.Value, token.Key)); subNodes.Add(Element2TreeNode(token.Value, token.Key));
@@ -261,7 +262,7 @@ public partial class MainViewForm : Form
break; break;
case "JArray": case "JArray":
JArray jArray = (JArray) element; JArray jArray = (JArray)element;
int i = 0; int i = 0;
subNodes.AddRange(jArray.Select(token => Element2TreeNode(token, $"[{i++}]"))); subNodes.AddRange(jArray.Select(token => Element2TreeNode(token, $"[{i++}]")));
@@ -272,22 +273,23 @@ public partial class MainViewForm : Form
break; break;
case "JValue": case "JValue":
JValue jValue = (JValue) element; JValue jValue = (JValue)element;
text += $": ({jValue.Type}) {jValue}"; text += $": ({jValue.Type}) {jValue}";
break; break;
default: default:
throw new Exception("clsJSON2Treeview can't interpret object:" + element.GetType().Name); throw new Exception(
"clsJSON2Treeview can't interpret object:" + element.GetType().Name
);
} }
TreeNode treeNode = new(text); TreeNode treeNode = new(text);
if (subNodes.Count > 0) if (subNodes.Count > 0)
{ {
treeNode.Nodes.AddRange(subNodes treeNode.Nodes.AddRange(
.OrderBy(node => node.Nodes.Count > 0 ? 1 : 0) subNodes.OrderBy(node => node.Nodes.Count > 0 ? 1 : 0).ToArray()
.ToArray() );
);
} }
return treeNode; return treeNode;
@@ -296,12 +298,11 @@ 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.ToString(); public string Name { get; set; } = e.Name[LocaleType.English];
public EntityRarity Rarity { get; set; } = e.Rarity; public EntityRarity Rarity { get; set; } = e.Rarity;
private readonly Entity _e = e; private readonly Entity _e = e;
public Entity GetEntity() => _e; public Entity GetEntity() => _e;
} }
} }