Compare commits

...
3 Commits
Author SHA1 Message Date
kozhilya 06d154eccc Обновление отображения 2025-04-27 12:22:52 +04:00
kozhilya 76167f1b24 Большой рефактор 2025-04-26 16:56:29 +04:00
kozhilya 9d0683a23f Немного работы 2025-04-26 10:53:50 +04:00
93 changed files with 3296 additions and 1063 deletions
+6
View File
@@ -4,6 +4,11 @@ root = true
# Файлы C# # Файлы C#
[*.cs] [*.cs]
#### Resharper ####
resharper_member_can_be_private_global_highlighting = none
#### Основные параметры EditorConfig #### #### Основные параметры EditorConfig ####
# Отступы и интервалы # Отступы и интервалы
@@ -265,3 +270,4 @@ dotnet_code_quality_unused_parameters = all:suggestion
dotnet_style_operator_placement_when_wrapping = beginning_of_line dotnet_style_operator_placement_when_wrapping = beginning_of_line
tab_width = 4 tab_width = 4
indent_size = 4 indent_size = 4
dotnet_diagnostic.CA1822.severity = none
+1
View File
@@ -10,6 +10,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pf2eDataScrapper", "Pf2eDat
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Элементы решения", "Элементы решения", "{754FC069-D67B-A9D7-50A1-8D1CA196D8F1}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Элементы решения", "Элементы решения", "{754FC069-D67B-A9D7-50A1-8D1CA196D8F1}"
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
README.md = README.md README.md = README.md
EndProjectSection EndProjectSection
EndProject EndProject
+5 -9
View File
@@ -5,21 +5,17 @@ namespace Pf2eDataScrapper.Importers;
public class DatabaseHelper public class DatabaseHelper
{ {
public Pf2eDbContext Context = new(); public readonly Pf2eDbContext Context = new();
public SlugHelper Slugger = new(); public readonly SlugHelper Slugger = new();
private int _id = 0; private int _id;
public int NextEntityId public int NextEntityId => ++_id;
{
get => ++_id;
set => _id = value;
}
private DatabaseHelper() private DatabaseHelper()
{ {
NextEntityId = Context.Entities.Any() ? Context.Entities.Max(e => e.Id) : 0; _id = Context.Entities.Any() ? Context.Entities.Max(e => e.Id) : 0;
} }
public string GenerateSlug(string name) public string GenerateSlug(string name)
@@ -18,7 +18,7 @@ internal class AonprdLoader(AonprdImporter importer)
foreach (KeyValuePair<string, JToken?> kvp in indexJson) foreach (KeyValuePair<string, JToken?> kvp in indexJson)
{ {
string file = kvp.Key.ToString(); string file = kvp.Key;
JArray items = (JArray)kvp.Value!; JArray items = (JArray)kvp.Value!;
foreach (JToken item in items) foreach (JToken item in items)
@@ -40,7 +40,7 @@ internal class AonprdLoader(AonprdImporter importer)
public IEnumerable<LoaderEntry> Entries(string[] types) public IEnumerable<LoaderEntry> Entries(string[] types)
{ {
foreach (string? file in Compendium.Files(types)) foreach (string file in Compendium.Files(types))
{ {
using StreamReader fileReader = new(Path.Combine(Importer.Dir, $"{file}.json")); using StreamReader fileReader = new(Path.Combine(Importer.Dir, $"{file}.json"));
JArray array = JArray.Parse(fileReader.ReadToEnd()); JArray array = JArray.Parse(fileReader.ReadToEnd());
@@ -1,13 +1,13 @@
using Konsole; using Konsole;
using Newtonsoft.Json.Linq;
using Pf2eDataScrapper.Importers.Implementations.Aonprd.Compendium; using Pf2eDataScrapper.Importers.Implementations.Aonprd.Compendium;
using Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors; 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;
internal class AonprdDataBuilder(AonprdLoader loader) internal class AonprdDataBuilder(AonprdLoader loader)
{ {
public Pf2eDatabase Database = Pf2eDatabase.Instance; public readonly Pf2eDatabase Database = Pf2eDatabase.Instance;
/// <summary> /// <summary>
/// Загрузчик данных. /// Загрузчик данных.
@@ -19,7 +19,7 @@ internal class AonprdDataBuilder(AonprdLoader loader)
/// </summary> /// </summary>
public AonprdCompendium Compendium => Loader.Compendium; public AonprdCompendium Compendium => Loader.Compendium;
public AonprdProcessorCollection Processors = new(); public readonly AonprdProcessorCollection Processors = new();
public void TruncateTables() public void TruncateTables()
{ {
@@ -30,42 +30,35 @@ internal class AonprdDataBuilder(AonprdLoader loader)
Console.WriteLine("Truncation completed!"); Console.WriteLine("Truncation completed!");
} }
public void Build() protected ConcurrentWriter Window = null!;
protected Window Output = null!;
protected ProgressBar ProcessProgressBar = null!;
protected ProgressBar SubProcessProgressBar = null!;
protected void ConfigureWindow()
{ {
Console.WriteLine("Building data..."); Window = new Window(0, 0, Console.WindowWidth, Console.WindowHeight).Concurrent();
ConcurrentWriter window = new Window( IConsole bars = Window.OpenBox(
0,
0,
Console.WindowWidth,
Console.WindowHeight
).Concurrent();
IConsole bars = window.OpenBox(
"Progress", "Progress",
Console.WindowWidth, Console.WindowWidth,
4, 4,
new BoxStyle { Body = Colors.WhiteOnBlack } new() { Body = Colors.WhiteOnBlack }
); );
Window output = new(0, 4, Console.WindowWidth, Console.WindowHeight - 4, window); Output = new(0, 4, Console.WindowWidth, Console.WindowHeight - 4, Window);
ProgressBar bar1 = new(bars, Processors.Items.Count, 15); ProcessProgressBar = new(bars, Processors.Items.Count, 15);
ProgressBar bar2 = new(bars, 1, 15);
int i = 0;
foreach (AonprdProcessorCollection.ProcessorItem item in Processors.Iterate()) SubProcessProgressBar = new(bars, 1, 15);
}
protected void RunProcessor(AonprdProcessorCollection.ProcessorItem item, JObject json)
{ {
bar1.Refresh(i++, $"{item.Attribute.Name}"); AonprdProcessorInput input = new(json);
bar2.Max = Compendium.Count([item.Attribute.Type]);
int j = 0;
foreach (AonprdLoader.LoaderEntry entry in Loader.Entries(item.Attribute.Type))
{
bar2.Refresh(j++, $"{entry.Id}");
AonprdProcessorInput input = new(entry.Json);
AbstractProcessor processor = (AbstractProcessor)item.Constructor.Invoke([input]); AbstractProcessor processor = (AbstractProcessor)item.Constructor.Invoke([input]);
try try
@@ -75,19 +68,41 @@ internal class AonprdDataBuilder(AonprdLoader loader)
} }
catch (LegacyItemException ex) catch (LegacyItemException ex)
{ {
output.WriteLine(ex.Message); Output.WriteLine(ex.Message);
} }
catch (SkipItemException ex) catch (SkipItemException ex)
{ {
output.WriteLine(ex.Message); Output.WriteLine(ex.Message);
} }
} }
bar2.Refresh(j, "Processor completed"); public void Build()
{
ConfigureWindow();
foreach (AonprdProcessorCollection.ProcessorItem item in Processors.Iterate())
{
ProcessProgressBar.Next(item.Attribute.Name);
if (item.Attribute.Type is null)
{
RunProcessor(item, []);
continue;
} }
bar1.Refresh(i, "Done!"); SubProcessProgressBar.Max = Compendium.Count([item.Attribute.Type]);
window.Clear(); foreach (AonprdLoader.LoaderEntry entry in Loader.Entries(item.Attribute.Type))
{
SubProcessProgressBar.Next($"{entry.Id}");
RunProcessor(item, entry.Json);
}
SubProcessProgressBar.Next("Ready");
}
ProcessProgressBar.Next("Done!");
Window.Clear();
} }
} }
@@ -1,11 +1,11 @@
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder;
[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
{ {
public readonly string Name = name; public readonly string Name = name;
public readonly string Type = type; public readonly string? Type = type;
public readonly IEnumerable<string> Dependencies = dependencies; public readonly IEnumerable<string> Dependencies = dependencies;
@@ -1,6 +1,7 @@
using System.Reflection; using System.Reflection;
using Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder;
internal class AonprdProcessorCollection internal class AonprdProcessorCollection
{ {
@@ -41,13 +42,13 @@ internal class AonprdProcessorCollection
); );
if (collision is not null) if (collision is not null)
{ {
throw new Exception( throw new(
$"Multiply attributes 'AonprdProcessor' have same name '{attribute.Name}'." $"Multiply attributes 'AonprdProcessor' have same name '{attribute.Name}'."
+ $" They are added to classes: '{type.FullName}' and '{collision.Constructor.DeclaringType!.FullName}'" + $" They are added to classes: '{type.FullName}' and '{collision.Constructor.DeclaringType!.FullName}'"
); );
} }
Items.Add(new ProcessorItem(attribute, constructor)); Items.Add(new(attribute, constructor));
} }
} }
@@ -65,7 +66,7 @@ internal class AonprdProcessorCollection
} }
IEnumerable<int> dependencyPriorities = item.Attribute.Dependencies.Select(d => IEnumerable<int> dependencyPriorities = item.Attribute.Dependencies.Select(d =>
priorities.TryGetValue(d, out int value) ? value : -1 priorities.GetValueOrDefault(d, -1)
); );
if (dependencyPriorities.Any(p => p == -1)) if (dependencyPriorities.Any(p => p == -1))
@@ -1,4 +1,5 @@
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Helpers;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder;
@@ -84,10 +85,10 @@ internal class AonprdProcessorInput(JObject input) : ProcessorInput(input)
public class ActionStruct public class ActionStruct
{ {
public int Actions = 0; public int Actions;
public int MaxActions = 0; public int MaxActions;
public bool IsPassive = false; public bool IsPassive;
public bool IsReaction = false; public bool IsReaction;
public bool IsFreeAction = false; public bool IsFreeAction;
} }
} }
@@ -0,0 +1,12 @@
using Pf2eModel.Model.Base.Targets;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Helpers;
internal class AonprdBuilderGlobals
{
public ExternalTarget AonprdTarget = null!;
private AonprdBuilderGlobals() { }
public static AonprdBuilderGlobals Instance = new AonprdBuilderGlobals();
}
@@ -1,6 +1,6 @@
using Pf2eModel.Model.Enum; using Pf2eModel.Model.Enum;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Helpers;
public static class AonprdEnumResolver public static class AonprdEnumResolver
{ {
@@ -0,0 +1,19 @@
using System.Text.RegularExpressions;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Helpers;
internal partial class StringHelper
{
private StringHelper() { }
public string ResolveSource(string input)
{
Match match = SourceSuffixRegex().Match(input);
return match.Captures.Count > 0 ? match.Captures[0].Value : "";
}
public static StringHelper Instance { get; } = new();
[GeneratedRegex(@"^\[.*?\]\(.*?\)\s+(.*)$")]
private static partial Regex SourceSuffixRegex();
}
@@ -0,0 +1,223 @@
using System.Security.Cryptography;
using System.Text;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Pf2eModel;
using Pf2eModel.Model;
using Pf2eModel.Model.Base;
using Pf2eModel.Model.Base.Source;
using Pf2eModel.Model.Base.Targets;
using Pf2eModel.Model.Common.I18n;
using Pf2eModel.Model.Enum;
using Pf2eModel.Model.Structure;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors;
internal abstract class AbstractEntityProcessor(AonprdProcessorInput input)
: AbstractProcessor(input)
{
public Entity Entity { get; } = new();
public abstract EntityType Type { get; }
public abstract IEntity Object { get; }
public abstract void Add();
public override void Process()
{
if (Input.GetOptional<JArray>("remaster_id") is not null)
{
string id = Input.GetRequired<string>("id");
string name = Input.GetRequired<string>("name");
ProcessLegacy(id, name);
throw new LegacyItemException(name);
}
AddEntity();
AddAlias();
AddSource();
AddExternal();
AddTraits();
}
public override void Save()
{
Add();
Context.SaveChanges();
}
protected string AddedDescription(string path) =>
$"\r\n\r\n========== [{path}] ==========\r\n\r\n{Input.GetOptional<string>(path) ?? ""}";
protected string AddedDescription(string[] paths) =>
string.Join("", paths.Select(AddedDescription));
protected virtual string GetDescription() => Input.GetRequired<string>("markdown");
protected virtual void AddEntity()
{
Entity.Id = Helper.NextEntityId;
Entity.Type = Type;
Entity.Name[LocaleType.English] = Input.GetRequired<string>("name");
Entity.Img = "";
Entity.Description = GetDescription();
Entity.Rarity = Input.EnumOptional("rarity", EntityRarity.Common);
Entity.Slug = Helper.GenerateSlug(Entity.Name[LocaleType.English]);
Entity.IsTranslated = false;
Entity.IsChecked = false;
Object.Entity = Entity;
Object.Id = Entity.Id;
Context.Entities.Add(Entity);
}
protected virtual void AddAlias()
{
JArray? legacyIdArray = Input.GetOptional<JArray>("legacy_id");
if (legacyIdArray is null)
{
return;
}
string legacyId = legacyIdArray[0].ToString();
if (LegacyAliases.TryGetValue(legacyId, out string? legacyName))
{
// if (legacyName != Entity.Name[LocaleType.English])
Entity.Name[LocaleType.LegacyEnglish] = legacyName;
LegacyAliases.Remove(legacyId);
}
else
{
RemasterWithoutLegacy.Add(legacyId, Entity);
}
}
protected virtual void AddSource()
{
string name = Input.GetRequired<string>("source[0]");
Source? source = Context.Sources.FirstOrDefault(s => s.Name == name);
if (source == null)
{
source = new()
{
Name = name,
Category = Input.EnumRequired<SourceCategory>("primary_source_category"),
ReleaseDate = DateOnly.Parse(Input.GetRequired<string>("release_date")),
};
Context.Sources.Add(source);
}
EntitySource entitySource = new()
{
Entity = Entity,
Source = source,
Suffix = StringHelper.ResolveSource(Input.GetRequired<string>("source_markdown")),
IsRemaster = Input.GetOptional<JArray>("legacy_id") is not null,
};
Context.EntitySources.Add(entitySource);
}
protected virtual void AddExternal()
{
EntityExternal external = new()
{
Entity = Entity,
Target = Globals.AonprdTarget,
Suffix = Input.GetRequired<string>("url"),
Md5 = Md5(Input.Input.ToString()),
};
Context.EntityExternals.Add(external);
}
protected virtual void AddTraits()
{
JToken? traitsJson = Input.Input.SelectToken("trait");
if (traitsJson?.Type != JTokenType.Array)
{
return;
}
IEnumerable<string> names = traitsJson.ToArray().Select(jToken => jToken.ToString());
List<Entity> traitEntities = Context
.Traits.Select(t => t.Entity)
// ReSharper disable once EntityFramework.UnsupportedServerSideFunctionCall
.Where(e => Pf2eDbContext.LocaleSearchArray(e.Name, names))
.ToList();
List<Trait> traits = Context
.Traits.Include(t => t.Entity)
.Where(t => traitEntities.Contains(t.Entity))
.ToList();
foreach (string name in names)
{
if (!traits.Any(t => t.Entity.Name.Contains(name)))
{
throw new Exception($"Trait '{name}' is not found!!!");
}
}
foreach (Trait trait in traits)
{
Entity.Traits.Add(trait);
}
}
protected void ProcessLegacy(string id, string name)
{
if (RemasterWithoutLegacy.TryGetValue(id, out Entity? remastered))
{
// if (remastered.ToString() != name)
remastered.Name[LocaleType.LegacyEnglish] = name;
RemasterWithoutLegacy.Remove(id);
}
else
{
LegacyAliases.Add(id, name);
}
}
public string GenerateExtra(params string[] args)
{
Dictionary<string, object?> data = [];
foreach (string key in args)
{
data.Add(key, Input.Input.SelectToken(key));
}
return JsonConvert.SerializeObject(data, new JsonSerializerSettings());
}
public static string Md5(string input, bool isLowercase = false)
{
using MD5 md5 = MD5.Create();
byte[] byteHash = md5.ComputeHash(Encoding.UTF8.GetBytes(input));
string hash = BitConverter.ToString(byteHash).Replace("-", "");
return isLowercase ? hash.ToLower() : hash;
}
protected static readonly Dictionary<string, string> LegacyAliases = [];
protected static readonly Dictionary<string, Entity> RemasterWithoutLegacy = [];
}
public class LegacyItemException(string name)
: Exception($"Item '{name}' is linked with newer version, skipping.") { }
public class SkipItemException(string name, string reason)
: Exception($"Item '{name}' is skipped, reason: {reason}.") { }
@@ -1,248 +1,21 @@
using System.Security.Cryptography; using Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Helpers;
using System.Text;
using System.Text.RegularExpressions;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Pf2eModel; using Pf2eModel;
using Pf2eModel.Model;
using Pf2eModel.Model.Base;
using Pf2eModel.Model.Base.Source;
using Pf2eModel.Model.Base.Targets;
using Pf2eModel.Model.Common.I18n;
using Pf2eModel.Model.Enum;
using Pf2eModel.Model.Structure;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors;
internal abstract partial class AbstractProcessor(AonprdProcessorInput input) internal abstract class AbstractProcessor(AonprdProcessorInput input)
{ {
protected DatabaseHelper Helper = DatabaseHelper.Instance; protected readonly DatabaseHelper Helper = DatabaseHelper.Instance;
protected readonly StringHelper StringHelper = StringHelper.Instance;
protected readonly AonprdBuilderGlobals Globals = AonprdBuilderGlobals.Instance;
public readonly Pf2eDbContext Context = DatabaseHelper.DbContext; public readonly Pf2eDbContext Context = DatabaseHelper.DbContext;
public readonly AonprdProcessorInput Input = input; public readonly AonprdProcessorInput Input = input;
public Entity Entity { get; set; } = new(); public abstract void Process();
public abstract EntityType Type { get; } public abstract void Save();
public abstract IEntity Object { get; }
protected abstract void Add();
public virtual void Process()
{
if (Input.GetOptional<JArray>("remaster_id") is not null)
{
string id = Input.GetRequired<string>("id");
string name = Input.GetRequired<string>("name");
ProcessLegacy(id, name);
throw new LegacyItemException(name);
}
AddEntity();
AddAlias();
AddSource();
AddExternal();
AddTraits();
}
public void Save()
{
Add();
Context.SaveChanges();
}
protected string AddedDescription(string path) =>
$"\r\n\r\n========== [{path}] ==========\r\n\r\n{Input.GetOptional<string>(path) ?? ""}";
protected string AddedDescription(string[] paths) =>
string.Join("", paths.Select(AddedDescription));
protected virtual string GetDescription() => Input.GetRequired<string>("markdown");
protected virtual void AddEntity()
{
Entity.Id = Helper.NextEntityId;
Entity.Type = Type;
Entity.Name[LocaleType.English] = Input.GetRequired<string>("name");
Entity.Img = "";
Entity.Description = GetDescription();
Entity.Rarity = Input.EnumOptional<EntityRarity>("rarity", EntityRarity.Common);
Entity.Slug = Helper.GenerateSlug(Entity.Name[LocaleType.English]);
Entity.IsTranslated = false;
Entity.IsChecked = false;
Object.Entity = Entity;
Object.Id = Entity.Id;
Context.Entities.Add(Entity);
}
protected virtual void AddAlias()
{
JArray? legacyIdArray = Input.GetOptional<JArray>("legacy_id");
if (legacyIdArray is null)
{
return;
}
string legacyId = legacyIdArray[0].ToString();
if (LegacyAliases.TryGetValue(legacyId, out string? legacyName))
{
// if (legacyName != Entity.Name[LocaleType.English])
Entity.Name[LocaleType.LegacyEnglish] = legacyName;
LegacyAliases.Remove(legacyId);
}
else
{
RemasterWithoutLegacy.Add(legacyId, Entity);
}
}
protected virtual void AddSource()
{
string name = Input.GetRequired<string>("source[0]");
Source? source = Context.Sources.FirstOrDefault(s => s.Name == name);
if (source == null)
{
source = new()
{
Name = name,
Category = Input.EnumRequired<SourceCategory>("primary_source_category"),
ReleaseDate = DateOnly.Parse(Input.GetRequired<string>("release_date")),
};
Context.Sources.Add(source);
}
string sourceMarkdown = Input.GetRequired<string>("source_markdown");
Match match = SourceSuffixRegex().Match(sourceMarkdown);
EntitySource entitySource = new()
{
Entity = Entity,
Source = source,
Suffix = ((match?.Captures.Count > 0) ? match?.Captures[0]?.Value : "") ?? "",
IsRemaster = Input.GetOptional<JArray>("legacy_id") is not null,
};
Context.EntitySources.Add(entitySource);
}
protected virtual void AddExternal()
{
if (AonprdTarget is null)
{
AonprdTarget = Context.ExternalTargets.FirstOrDefault(t =>
t.Name == AonprdImporter.SourceName
);
AonprdTarget ??= new()
{
Name = AonprdImporter.SourceName,
Icon = "http://www.google.com/s2/favicons?domain=2e.aonprd.com",
Prefix = "https://2e.aonprd.com",
};
}
EntityExternal external = new()
{
Entity = Entity,
Target = AonprdTarget,
Suffix = Input.GetRequired<string>("url"),
Md5 = Md5(Input.Input.ToString()),
};
Context.EntityExternals.Add(external);
}
protected virtual void AddTraits()
{
JToken? traitsJson = Input.Input.SelectToken("trait");
if (traitsJson?.Type != JTokenType.Array)
{
return;
}
IEnumerable<string> names = traitsJson.ToArray().Select(jToken => jToken.ToString());
List<Entity> traitEntities = Context
.Traits.Select(t => t.Entity)
.Where(e => Pf2eDbContext.LocaleSearchArray(e.Name, names))
.ToList();
List<Trait> traits = Context
.Traits.Include(t => t.Entity)
.Where(t => traitEntities.Contains(t.Entity))
.ToList();
foreach (string name in names)
{
if (!traits.Any(t => t.Entity.Name.Contains(name)))
{
throw new Exception($"Trait '{name}' is not found!!!");
}
}
foreach (Trait trait in traits)
{
Entity.Traits.Add(trait);
}
}
protected void ProcessLegacy(string id, string name)
{
if (RemasterWithoutLegacy.TryGetValue(id, out Entity? remastered))
{
// if (remastered.ToString() != name)
remastered.Name[LocaleType.LegacyEnglish] = name;
RemasterWithoutLegacy.Remove(id);
}
else
{
LegacyAliases.Add(id, name);
}
}
public string GenerateExtra(params string[] args)
{
Dictionary<string, object?> data = [];
foreach (string key in args)
{
data.Add(key, Input.Input.SelectToken(key));
}
return JsonConvert.SerializeObject(data, new JsonSerializerSettings() { });
}
public static string Md5(string input, bool isLowercase = false)
{
using MD5 md5 = MD5.Create();
byte[] byteHash = md5.ComputeHash(Encoding.UTF8.GetBytes(input));
string hash = BitConverter.ToString(byteHash).Replace("-", "");
return isLowercase ? hash.ToLower() : hash;
}
protected static ExternalTarget? AonprdTarget = null;
protected static Dictionary<string, string> LegacyAliases = [];
protected static Dictionary<string, Entity> RemasterWithoutLegacy = [];
[GeneratedRegex(@"^\[.*?\]\(.*?\)\s+(.*)$")]
private static partial Regex SourceSuffixRegex();
public class LegacyItemException(string name)
: Exception($"Item '{name}' is linked with newer version, skipping.") { }
public class SkipItemException(string name, string reason)
: Exception($"Item '{name}' is skipped, reason: {reason}.") { }
} }
@@ -1,10 +1,4 @@
using System; using Pf2eModel;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Pf2eModel;
using Pf2eModel.Model;
using Pf2eModel.Model.Base; using Pf2eModel.Model.Base;
using Pf2eModel.Model.Common.I18n; using Pf2eModel.Model.Common.I18n;
using Pf2eModel.Model.Enum; using Pf2eModel.Model.Enum;
@@ -13,7 +7,7 @@ using Pf2eModel.Model.Structure;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors;
internal abstract class AbstractSubclassProcessor(AonprdProcessorInput input) internal abstract class AbstractSubclassProcessor(AonprdProcessorInput input)
: AbstractProcessor(input) : AbstractEntityProcessor(input)
{ {
public override EntityType Type => EntityType.Subclass; public override EntityType Type => EntityType.Subclass;
@@ -23,7 +17,7 @@ internal abstract class AbstractSubclassProcessor(AonprdProcessorInput input)
public abstract string GroupName { get; } public abstract string GroupName { get; }
protected override void Add() => Context.Subclasses.Add(Object); public override void Add() => Context.Subclasses.Add(Object);
public override void Process() public override void Process()
{ {
@@ -37,14 +31,18 @@ internal abstract class AbstractSubclassProcessor(AonprdProcessorInput input)
public SubclassGroup GetGroup() public SubclassGroup GetGroup()
{ {
SubclassGroup? group = Context.SubclassGroups.FirstOrDefault(g => SubclassGroup? group = Context.SubclassGroups.FirstOrDefault(g =>
// ReSharper disable once EntityFramework.UnsupportedServerSideFunctionCall
Pf2eDbContext.LocaleSearch(g.Entity.Name, GroupName) Pf2eDbContext.LocaleSearch(g.Entity.Name, GroupName)
); );
if (group is null) if (group is not null)
{ {
Class? cl = return group;
(ClassName is not null) }
Class? cl = ClassName is not null
? Context.Classes.FirstOrDefault(c => ? Context.Classes.FirstOrDefault(c =>
// ReSharper disable once EntityFramework.UnsupportedServerSideFunctionCall
Pf2eDbContext.LocaleSearch(c.Entity.Name, GroupName) Pf2eDbContext.LocaleSearch(c.Entity.Name, GroupName)
) )
: null; : null;
@@ -58,29 +56,14 @@ internal abstract class AbstractSubclassProcessor(AonprdProcessorInput input)
Rarity = EntityRarity.Common, Rarity = EntityRarity.Common,
IsTranslated = false, IsTranslated = false,
IsChecked = false, IsChecked = false,
Name = { [LocaleType.English] = GroupName },
Slug = Helper.GenerateSlug(GroupName),
}; };
groupEntity.Name[LocaleType.English] = GroupName;
groupEntity.Slug = Helper.GenerateSlug(GroupName);
Context.Entities.Add(groupEntity); Context.Entities.Add(groupEntity);
/* group = new() { Entity = groupEntity, Class = cl };
Entity.Id = Helper.NextEntityId;
Entity.Type = Type;
Entity.Name[LocaleType.English] = Input.GetRequired<string>("name");
Entity.Img = "";
Entity.Description = GetDescription();
Entity.Rarity = Input.EnumOptional<EntityRarity>("rarity", EntityRarity.Common);
Entity.Slug = Helper.GenerateSlug(Entity.Name[LocaleType.English]);
Entity.IsTranslated = false;
Entity.IsChecked = false;
*/
group = new SubclassGroup() { Entity = groupEntity, Class = cl };
Context.SubclassGroups.Add(group); Context.SubclassGroups.Add(group);
}
return group; return group;
} }
@@ -0,0 +1,26 @@
using Pf2eModel.Model.Base.Targets;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Common;
[AonprdProcessor("external", null, [])]
internal class ExternalProcessor(AonprdProcessorInput input) : AbstractProcessor(input)
{
public ExternalTarget AonprdTarget = null!;
public override void Process()
{
AonprdTarget = new()
{
Name = AonprdImporter.SourceName,
Icon = "http://www.google.com/s2/favicons?domain=2e.aonprd.com",
Prefix = "https://2e.aonprd.com",
};
}
public override void Save()
{
Context.ExternalTargets.Add(AonprdTarget);
Globals.AonprdTarget = AonprdTarget;
}
}
@@ -1,29 +1,25 @@
using Pf2eModel.Model; using Pf2eModel.Model.Base.Source;
using Pf2eModel.Model.Base.Source;
using Pf2eModel.Model.Enum; using Pf2eModel.Model.Enum;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Common;
[AonprdProcessor("source", new string[] { })] [AonprdProcessor("source", [])]
internal class SourceProcessor(AonprdProcessorInput input) : AbstractProcessor(input) internal class SourceProcessor(AonprdProcessorInput input) : AbstractProcessor(input)
{ {
public override EntityType Type => EntityType.Unknown; protected Source Source = null!;
public override IEntity? Object => null;
protected override void Add() { }
public override void Process() public override void Process()
{ {
// base.Process(); Source = new()
Source source = new()
{ {
Name = Input.GetRequired<string>("name"), Name = Input.GetRequired<string>("name"),
Category = Input.EnumRequired<SourceCategory>("primary_source_category"), Category = Input.EnumRequired<SourceCategory>("primary_source_category"),
ReleaseDate = DateOnly.Parse(Input.GetRequired<string>("release_date")), ReleaseDate = DateOnly.Parse(Input.GetRequired<string>("release_date")),
}; };
}
Context.Sources.Add(source); public override void Save()
{
Context.Sources.Add(Source);
} }
} }
@@ -0,0 +1,20 @@
using Pf2eModel.Model.Common;
using Pf2eModel.Model.Enum;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Common;
[AonprdProcessor("skill", [])]
internal class TrainingProcessor(AonprdProcessorInput input) : AbstractProcessor(input)
{
public Training Training = null!;
public override void Process()
{
Training = new() { Name = Input.GetRequired<string>("name"), Type = TrainingType.Skill };
}
public override void Save()
{
Context.Trainings.Add(Training);
}
}
@@ -1,9 +1,4 @@
using System; using Pf2eModel.Model.Structure;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Pf2eModel.Model.Structure;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Subclasses; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Subclasses;
@@ -12,7 +7,7 @@ internal class ApparitionProcessor(AonprdProcessorInput input) : AbstractSubclas
{ {
public override Subclass Object { get; } = new(); public override Subclass Object { get; } = new();
public override string? ClassName => "Animist"; public override string ClassName => "Animist";
public override string GroupName => "Apparition"; public override string GroupName => "Apparition";
@@ -3,13 +3,13 @@
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types;
[AonprdProcessor("action")] [AonprdProcessor("action")]
internal class ActionProcessor(AonprdProcessorInput input) : AbstractProcessor(input) internal class ActionProcessor(AonprdProcessorInput input) : AbstractEntityProcessor(input)
{ {
public override EntityType Type => EntityType.Action; public override EntityType Type => EntityType.Action;
public override Pf2eModel.Model.Structure.Action Object { get; } = new(); public override Pf2eModel.Model.Structure.Action Object { get; } = new();
protected override void Add() => Context.Actions.Add(Object); public override void Add() => Context.Actions.Add(Object);
public override void Process() public override void Process()
{ {
@@ -24,7 +24,7 @@ internal class ActionProcessor(AonprdProcessorInput input) : AbstractProcessor(i
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");
} }
@@ -1,19 +1,17 @@
using LinqToDB; using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Linq;
using Pf2eModel.Model.Enum; using Pf2eModel.Model.Enum;
using Pf2eModel.Model.Structure; using Pf2eModel.Model.Structure;
using Pf2eModel.Model.Structure.AncestryRelation;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types; 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) : AbstractEntityProcessor(input)
{ {
public override EntityType Type => EntityType.Ancestry; public override EntityType Type => EntityType.Ancestry;
public override Ancestry Object { get; } = new(); public override Ancestry Object { get; } = new();
protected override void Add() => Context.Ancestries.Add(Object); public override void Add() => Context.Ancestries.Add(Object);
public override void Process() public override void Process()
{ {
@@ -36,7 +34,7 @@ internal class AncestryProcessor(AonprdProcessorInput input) : AbstractProcessor
Object.Speed = Convert.ToInt32(Input.GetOptional<int>("speed.land")); Object.Speed = Convert.ToInt32(Input.GetOptional<int>("speed.land"));
Object.Vision = Input.EnumOptional<AncestryVision>("vision", AncestryVision.Normal); Object.Vision = Input.EnumOptional("vision", AncestryVision.Normal);
AddBoosts("attribute", true); AddBoosts("attribute", true);
AddBoosts("attribute_flaw", false); AddBoosts("attribute_flaw", false);
@@ -59,14 +57,14 @@ internal class AncestryProcessor(AonprdProcessorInput input) : AbstractProcessor
foreach (string boost in boosts) foreach (string boost in boosts)
{ {
AncestryBoost ancestryBoost = new() Context.AncestryBoosts.Add(
new()
{ {
Ancestry = Object, Ancestry = Object,
Boost = isBoost, Boost = isBoost,
Value = Input.Enum<AttributeValues>(boost), Value = Input.Enum<AttributeValues>(boost),
}; }
);
Context.AncestryBoosts.Add(ancestryBoost);
} }
} }
} }
@@ -4,13 +4,13 @@ using Pf2eModel.Model.Structure;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types;
[AonprdProcessor("archetype")] [AonprdProcessor("archetype")]
internal class ArchetypeProcessor(AonprdProcessorInput input) : AbstractProcessor(input) internal class ArchetypeProcessor(AonprdProcessorInput input) : AbstractEntityProcessor(input)
{ {
public override EntityType Type => EntityType.Archetype; public override EntityType Type => EntityType.Archetype;
public override Archetype Object { get; } = new(); public override Archetype Object { get; } = new();
protected override void Add() => Context.Archetypes.Add(Object); public override void Add() => Context.Archetypes.Add(Object);
public override void Process() public override void Process()
{ {
@@ -6,13 +6,13 @@ using Pf2eModel.Model.Structure.BackgroundRelation;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types;
[AonprdProcessor("background")] [AonprdProcessor("background")]
internal class BackgroundProcessor(AonprdProcessorInput input) : AbstractProcessor(input) internal class BackgroundProcessor(AonprdProcessorInput input) : AbstractEntityProcessor(input)
{ {
public override EntityType Type => EntityType.Background; public override EntityType Type => EntityType.Background;
public override Background Object { get; } = new(); public override Background Object { get; } = new();
protected override void Add() => Context.Backgrounds.Add(Object); public override void Add() => Context.Backgrounds.Add(Object);
public override void Process() public override void Process()
{ {
@@ -1,6 +1,4 @@
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using LinqToDB;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using Pf2eModel.Model.Common; using Pf2eModel.Model.Common;
using Pf2eModel.Model.Enum; using Pf2eModel.Model.Enum;
@@ -10,13 +8,13 @@ using Pf2eModel.Model.Structure.ClassRelation;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types; 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) : AbstractEntityProcessor(input)
{ {
public override EntityType Type => EntityType.Class; public override EntityType Type => EntityType.Class;
public override Class Object { get; } = new(); public override Class Object { get; } = new();
protected override void Add() => Context.Classes.Add(Object); public override void Add() => Context.Classes.Add(Object);
public override void Process() public override void Process()
{ {
@@ -156,7 +154,6 @@ internal class ClassProcessor(AonprdProcessorInput input) : AbstractProcessor(in
.Where(s => s != "") .Where(s => s != "")
.Select(s => string.Concat(s[0].ToString().ToUpper(), s.AsSpan(1))) .Select(s => string.Concat(s[0].ToString().ToUpper(), s.AsSpan(1)))
.ToArray(); .ToArray();
;
} }
protected ICollection<ClassTraining> GetSaveTrainings() protected ICollection<ClassTraining> GetSaveTrainings()
@@ -6,13 +6,13 @@ using Pf2eModel.Model.Structure;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types; 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) : AbstractEntityProcessor(input)
{ {
public override EntityType Type => EntityType.Feat; public override EntityType Type => EntityType.Feat;
public override Feat Object { get; } = new(); public override Feat Object { get; } = new();
protected override void Add() => Context.Feats.Add(Object); public override void Add() => Context.Feats.Add(Object);
public override void Process() public override void Process()
{ {
@@ -47,6 +47,7 @@ internal class FeatProcessor(AonprdProcessorInput input) : AbstractProcessor(inp
Input.GetOptional<JArray>("archetype")?.Select(t => t.ToString()).ToArray() ?? []; Input.GetOptional<JArray>("archetype")?.Select(t => t.ToString()).ToArray() ?? [];
Archetype[] archetypes = Context Archetype[] archetypes = Context
// ReSharper disable once EntityFramework.UnsupportedServerSideFunctionCall
.Archetypes.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();
@@ -56,7 +57,7 @@ internal class FeatProcessor(AonprdProcessorInput input) : AbstractProcessor(inp
Object.Category = ActionCategory.Archetype; Object.Category = ActionCategory.Archetype;
} }
foreach (Archetype? archetype in archetypes) foreach (Archetype archetype in archetypes)
{ {
Object.Archetypes.Add(archetype); Object.Archetypes.Add(archetype);
@@ -4,16 +4,11 @@ using Pf2eModel.Model.Structure;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types;
[AonprdProcessor("language")] [AonprdProcessor("language")]
internal class LanguageProcessor(AonprdProcessorInput input) : AbstractProcessor(input) internal class LanguageProcessor(AonprdProcessorInput input) : AbstractEntityProcessor(input)
{ {
public override EntityType Type => EntityType.Language; public override EntityType Type => EntityType.Language;
public override Language Object { get; } = new(); public override Language Object { get; } = new();
protected override void Add() => Context.Languages.Add(Object); public override void Add() => Context.Languages.Add(Object);
public override void Process()
{
base.Process();
}
} }
@@ -1,28 +0,0 @@
using Pf2eModel.Model;
using Pf2eModel.Model.Common;
using Pf2eModel.Model.Enum;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types;
[AonprdProcessor("skill", new string[] { })]
internal class TrainingProcessor(AonprdProcessorInput input) : AbstractProcessor(input)
{
public override EntityType Type => EntityType.Unknown;
public override IEntity? Object => null;
protected override void Add() { }
public override void Process()
{
// base.Process();
Training training = new()
{
Name = Input.GetRequired<string>("name"),
Type = TrainingType.Skill,
};
Context.Trainings.Add(training);
}
}
@@ -3,14 +3,14 @@ using Pf2eModel.Model.Structure;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types;
[AonprdProcessor("trait", new string[] { })] [AonprdProcessor("trait", ["external"])]
internal class TraitProcessor(AonprdProcessorInput input) : AbstractProcessor(input) internal class TraitProcessor(AonprdProcessorInput input) : AbstractEntityProcessor(input)
{ {
public override EntityType Type => EntityType.Trait; public override EntityType Type => EntityType.Trait;
public override Trait Object { get; } = new(); public override Trait Object { get; } = new();
protected override void Add() => Context.Traits.Add(Object); public override void Add() => Context.Traits.Add(Object);
public override void Process() public override void Process()
{ {
@@ -46,7 +46,7 @@ internal class CompendiumFolder
if (json.TryGetValue("_id", out JToken? id)) if (json.TryGetValue("_id", out JToken? id))
{ {
// Вложенная папка через _folders.json // Вложенная папка через _folders.json
Id = id!.ToString(); Id = id.ToString();
Name = json["name"]!.ToString(); Name = json["name"]!.ToString();
Path = null; Path = null;
IsContained = true; IsContained = true;
@@ -22,12 +22,12 @@ internal class CompendiumLoader(Pf2EFoundryImporter importer)
/// <summary> /// <summary>
/// Счётчик папок /// Счётчик папок
/// </summary> /// </summary>
private int _folderCount = 0; private int _folderCount;
/// <summary> /// <summary>
/// Общее количество элементов в компендиуме /// Общее количество элементов в компендиуме
/// </summary> /// </summary>
public int ItemCount { get; protected set; } = 0; public int ItemCount { get; protected set; }
/// <summary> /// <summary>
/// Обработка папок компендиума в поле `packFolders`. /// Обработка папок компендиума в поле `packFolders`.
@@ -48,7 +48,7 @@ internal class CompendiumLoader(Pf2EFoundryImporter importer)
if (obj.TryGetValue("packs", out JToken? packs)) if (obj.TryGetValue("packs", out JToken? packs))
{ {
foreach (JToken jToken in packs!) foreach (JToken jToken in packs)
{ {
string packId = jToken.ToString(); string packId = jToken.ToString();
CompendiumFolder? child = Collection.GetById(packId); CompendiumFolder? child = Collection.GetById(packId);
@@ -62,7 +62,7 @@ internal class CompendiumLoader(Pf2EFoundryImporter importer)
if (obj.TryGetValue("folders", out JToken? innerFolders)) if (obj.TryGetValue("folders", out JToken? innerFolders))
{ {
ProcessPackFolders((JArray)innerFolders!, folder); ProcessPackFolders((JArray)innerFolders, folder);
} }
} }
@@ -90,7 +90,7 @@ internal class CompendiumLoader(Pf2EFoundryImporter importer)
} }
// Поиск _folders.json // Поиск _folders.json
foreach (CompendiumFolder? parent in Collection.ToList()) foreach (CompendiumFolder parent in Collection.ToList())
{ {
if (parent.Path is null) if (parent.Path is null)
{ {
@@ -1,5 +1,6 @@
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using Pf2eModel; using Pf2eModel;
using Pf2eModel.Model; using Pf2eModel.Model;
@@ -39,10 +40,7 @@ internal abstract class AbstractProcessor(ProcessorInput input)
Entity.IsChecked = false; Entity.IsChecked = false;
Entity.Slug = Helper.GenerateSlug(Entity.Name[LocaleType.English]); Entity.Slug = Helper.GenerateSlug(Entity.Name[LocaleType.English]);
Entity.Rarity = Input.EnumOptional<EntityRarity>( Entity.Rarity = Input.EnumOptional("system.traits.rarity", EntityRarity.Common);
"system.traits.rarity",
EntityRarity.Common
);
Object.Id = Entity.Id; Object.Id = Entity.Id;
@@ -140,16 +138,19 @@ internal abstract class AbstractProcessor(ProcessorInput input)
protected IEnumerable<Trait> GetTraits(IEnumerable<string> names) protected IEnumerable<Trait> GetTraits(IEnumerable<string> names)
{ {
List<Trait> result = Context List<Trait> result = Context
.Traits.Where(t => names.Contains(t.Entity.ToString())) .Traits.Include(t => t.Entity)
.ThenInclude(e => e.Name)
// ReSharper disable once EntityFramework.UnsupportedServerSideFunctionCall
.Where(t => Pf2eDbContext.LocaleSearchArray(t.Entity.Name, names))
.ToList(); .ToList();
/*
foreach (string name in names) foreach (string name in names)
{ {
if (result.Any(t => t.Entity.Name.Contains(name))) if (result.Any(t => t.Entity.Name.Contains(name)))
{ {
continue; continue;
} }
/*
Trait trait = new() Trait trait = new()
{ {
Name = name, Name = name,
@@ -159,8 +160,8 @@ internal abstract class AbstractProcessor(ProcessorInput input)
}; };
Context.Traits.Add(trait); Context.Traits.Add(trait);
result.Add(trait); result.Add(trait);
*/
} }
*/
return result; return result;
} }
@@ -24,7 +24,7 @@ internal class ConditionProcessor(ProcessorInput input) : AbstractProcessor(inpu
}; };
Object.Extra = extra.ToString(Newtonsoft.Json.Formatting.None); Object.Extra = extra.ToString(Newtonsoft.Json.Formatting.None);
Object.Group = Input.EnumOptional<ConditionGroup>("system.group", ConditionGroup.Unknown); Object.Group = Input.EnumOptional("system.group", ConditionGroup.Unknown);
Context.Conditions.Add(Object); Context.Conditions.Add(Object);
} }
@@ -2,10 +2,6 @@
namespace Pf2eDataScrapper.Importers.Repositories; namespace Pf2eDataScrapper.Importers.Repositories;
/// <summary>
/// Загрузчик данных из git-репозиториев
/// </summary>
/// <param name="repositoryDirPath"></param>
internal class GitRepository(AbstractImporter importer, string url) internal class GitRepository(AbstractImporter importer, string url)
: AbstractImporterRepository(importer) : AbstractImporterRepository(importer)
{ {
+9 -22
View File
@@ -11,7 +11,7 @@ internal class SchemaNode
/// <summary> /// <summary>
/// Отображаемое имя элемента. /// Отображаемое имя элемента.
/// </summary> /// </summary>
public string Name = ""; public readonly string Name = "";
/// <summary> /// <summary>
/// Путь к элементу. /// Путь к элементу.
@@ -21,12 +21,12 @@ internal class SchemaNode
/// <summary> /// <summary>
/// Родительский элемент. /// Родительский элемент.
/// </summary> /// </summary>
public SchemaNode? Parent = null; public readonly SchemaNode? Parent;
/// <summary> /// <summary>
/// Указание, является ли элемент корневым. /// Указание, является ли элемент корневым.
/// </summary> /// </summary>
public bool Root = false; public readonly bool Root;
/// <summary> /// <summary>
/// Корневой элемент схемы данных. /// Корневой элемент схемы данных.
@@ -36,12 +36,12 @@ internal class SchemaNode
/// <summary> /// <summary>
/// Типы данных, использующиеся в элементе. /// Типы данных, использующиеся в элементе.
/// </summary> /// </summary>
public List<AbstractSchemaType> Types = []; public readonly List<AbstractSchemaType> Types = [];
/// <summary> /// <summary>
/// Указание, может ли этот элемент отсутсвовать в структуре данных. /// Указание, может ли этот элемент отсутсвовать в структуре данных.
/// </summary> /// </summary>
public bool IsNullable = false; public bool IsNullable;
/// <summary> /// <summary>
/// Объект для определения типа поля в схеме данных. /// Объект для определения типа поля в схеме данных.
@@ -51,7 +51,7 @@ internal class SchemaNode
/// <summary> /// <summary>
/// Количество значений, добавленных в тип. /// Количество значений, добавленных в тип.
/// </summary> /// </summary>
public int Values { get; protected set; } = 0; public int Values { get; protected set; }
/// <summary> /// <summary>
/// Указание, является ли тип элемента простым. /// Указание, является ли тип элемента простым.
@@ -93,14 +93,11 @@ internal class SchemaNode
Values++; Values++;
foreach (AbstractSchemaType type in Types) foreach (AbstractSchemaType type in Types.Where(type => type.IsCompatibleValue(value)))
{
if (type.IsCompatibleValue(value))
{ {
type.AddValue(value); type.AddValue(value);
return; return;
} }
}
AbstractSchemaType newType = Resolver.Resolve(this, value); AbstractSchemaType newType = Resolver.Resolve(this, value);
@@ -161,7 +158,7 @@ internal class SchemaNode
foreach (AbstractSchemaType type in Types) foreach (AbstractSchemaType type in Types)
{ {
types.Add(Indent(type?.ToString() ?? "")); types.Add(Indent(type.ToString()));
} }
return FormatYaml(result, new() { ["types"] = "- " + string.Join("\r\n- ", types) }); return FormatYaml(result, new() { ["types"] = "- " + string.Join("\r\n- ", types) });
@@ -223,20 +220,10 @@ internal class SchemaNode
{ {
Dictionary<string, string> result = []; Dictionary<string, string> result = [];
foreach (Dictionary<string, string> item in input) foreach (KeyValuePair<string, string> kvp in input.SelectMany(item => item))
{
foreach (KeyValuePair<string, string> kvp in item)
{
if (result.ContainsKey(kvp.Key))
{ {
result[kvp.Key] = kvp.Value; result[kvp.Key] = kvp.Value;
} }
else
{
result.Add(kvp.Key, kvp.Value);
}
}
}
return result; return result;
} }
@@ -13,7 +13,7 @@ using Pf2eModel.Model.Enum;
namespace Pf2eModel.Migrations namespace Pf2eModel.Migrations
{ {
[DbContext(typeof(Pf2eDbContext))] [DbContext(typeof(Pf2eDbContext))]
[Migration("20250425111349_1")] [Migration("20250426125550_1")]
partial class _1 partial class _1
{ {
/// <inheritdoc /> /// <inheritdoc />
@@ -182,11 +182,18 @@ namespace Pf2eModel.Migrations
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id")); NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Name")
.IsRequired()
.HasColumnType("jsonb")
.HasColumnName("name")
.HasColumnOrder(2);
b.Property<string>("Slug") b.Property<string>("Slug")
.IsRequired() .IsRequired()
.HasColumnType("text") .HasMaxLength(255)
.HasColumnType("character varying(255)")
.HasColumnName("slug") .HasColumnName("slug")
.HasColumnOrder(2); .HasColumnOrder(3);
b.HasKey("Id") b.HasKey("Id")
.HasName("pk_keywords"); .HasName("pk_keywords");
@@ -1457,7 +1464,8 @@ namespace Pf2eModel.Migrations
b.Property<string>("Extra") b.Property<string>("Extra")
.IsRequired() .IsRequired()
.HasColumnType("text") .HasColumnType("text")
.HasColumnName("extra"); .HasColumnName("extra")
.HasColumnOrder(4);
b.Property<int>("GroupId") b.Property<int>("GroupId")
.HasColumnType("integer") .HasColumnType("integer")
@@ -91,7 +91,8 @@ 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),
slug = table.Column<string>(type: "text", nullable: false) name = table.Column<string>(type: "jsonb", nullable: false),
slug = table.Column<string>(type: "character varying(255)", maxLength: 255, nullable: false)
}, },
constraints: table => constraints: table =>
{ {
@@ -179,11 +179,18 @@ namespace Pf2eModel.Migrations
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id")); NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Name")
.IsRequired()
.HasColumnType("jsonb")
.HasColumnName("name")
.HasColumnOrder(2);
b.Property<string>("Slug") b.Property<string>("Slug")
.IsRequired() .IsRequired()
.HasColumnType("text") .HasMaxLength(255)
.HasColumnType("character varying(255)")
.HasColumnName("slug") .HasColumnName("slug")
.HasColumnOrder(2); .HasColumnOrder(3);
b.HasKey("Id") b.HasKey("Id")
.HasName("pk_keywords"); .HasName("pk_keywords");
@@ -1454,7 +1461,8 @@ namespace Pf2eModel.Migrations
b.Property<string>("Extra") b.Property<string>("Extra")
.IsRequired() .IsRequired()
.HasColumnType("text") .HasColumnType("text")
.HasColumnName("extra"); .HasColumnName("extra")
.HasColumnOrder(4);
b.Property<int>("GroupId") b.Property<int>("GroupId")
.HasColumnType("integer") .HasColumnType("integer")
+3 -2
View File
@@ -12,13 +12,13 @@ namespace Pf2eModel.Model.Base;
public class Entity public class Entity
{ {
[System.ComponentModel.DataAnnotations.Key] [Key]
[Column(Order = 1)] [Column(Order = 1)]
public int Id { get; set; } public int Id { get; set; }
[Column(Order = 2)] [Column(Order = 2)]
[Comment("Отображаемое имя")] [Comment("Отображаемое имя")]
public LocaleString Name { get; set; } = new(); public LocaleString Name { get; set; } = [];
[Column(Order = 3)] [Column(Order = 3)]
[Comment("Тип объекта")] [Comment("Тип объекта")]
@@ -36,6 +36,7 @@ public class Entity
[Column(Order = 6)] [Column(Order = 6)]
[Comment("Текстовое описание объекта")] [Comment("Текстовое описание объекта")]
// ReSharper disable once EntityFramework.ModelValidation.UnlimitedStringLength
public string Description { get; set; } = null!; public string Description { get; set; } = null!;
[Column(Order = 7)] [Column(Order = 7)]
+8 -3
View File
@@ -1,9 +1,10 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using Pf2eModel.Model.Common.I18n;
namespace Pf2eModel.Model.Base.Keywords; namespace Pf2eModel.Model.Base.Keywords;
public partial class Keyword public class Keyword
{ {
[Key] [Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)] [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
@@ -11,10 +12,14 @@ public partial class Keyword
public int Id { get; set; } public int Id { get; set; }
[Column(Order = 2)] [Column(Order = 2)]
public LocaleString Name = [];
[Column(Order = 3)]
[MaxLength(Pf2eDbContext.NameLength)]
public string Slug { get; set; } = null!; public string Slug { get; set; } = null!;
[Newtonsoft.Json.JsonIgnore] [Newtonsoft.Json.JsonIgnore]
public virtual ICollection<Entity> Entities { get; set; } = []; public ICollection<Entity> Entities { get; set; } = [];
public virtual ICollection<KeywordAlias> Aliases { get; set; } = []; public ICollection<KeywordAlias> Aliases { get; set; } = [];
} }
@@ -3,7 +3,7 @@ using System.ComponentModel.DataAnnotations.Schema;
namespace Pf2eModel.Model.Base.Keywords; namespace Pf2eModel.Model.Base.Keywords;
public partial class KeywordAlias public class KeywordAlias
{ {
[Key] [Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)] [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
@@ -18,5 +18,5 @@ public partial class KeywordAlias
public string Value { get; set; } = null!; public string Value { get; set; } = null!;
[Newtonsoft.Json.JsonIgnore] [Newtonsoft.Json.JsonIgnore]
public virtual Keyword Keyword { get; set; } = null!; public Keyword Keyword { get; set; } = null!;
} }
+3 -3
View File
@@ -5,7 +5,7 @@ using Microsoft.EntityFrameworkCore;
namespace Pf2eModel.Model.Base.Source; namespace Pf2eModel.Model.Base.Source;
[PrimaryKey(nameof(EntityId), nameof(SourceId))] [PrimaryKey(nameof(EntityId), nameof(SourceId))]
public partial class EntitySource public class EntitySource
{ {
[Column(Order = 1)] [Column(Order = 1)]
public int EntityId { get; set; } public int EntityId { get; set; }
@@ -23,7 +23,7 @@ public partial class EntitySource
public bool IsRemaster { get; set; } public bool IsRemaster { get; set; }
[Newtonsoft.Json.JsonIgnore] [Newtonsoft.Json.JsonIgnore]
public virtual Entity Entity { get; set; } = null!; public Entity Entity { get; set; } = null!;
public virtual Source Source { get; set; } = null!; public Source Source { get; set; } = null!;
} }
+1 -1
View File
@@ -4,7 +4,7 @@ using Pf2eModel.Model.Enum;
namespace Pf2eModel.Model.Base.Source; namespace Pf2eModel.Model.Base.Source;
public partial class Source public class Source
{ {
[Key] [Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)] [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
@@ -5,7 +5,7 @@ using Microsoft.EntityFrameworkCore;
namespace Pf2eModel.Model.Base.Targets; namespace Pf2eModel.Model.Base.Targets;
[PrimaryKey(nameof(EntityId), nameof(TargetId))] [PrimaryKey(nameof(EntityId), nameof(TargetId))]
public partial class EntityExternal public class EntityExternal
{ {
[Column(Order = 0)] [Column(Order = 0)]
public int EntityId { get; set; } public int EntityId { get; set; }
@@ -3,7 +3,7 @@ using System.ComponentModel.DataAnnotations.Schema;
namespace Pf2eModel.Model.Base.Targets; namespace Pf2eModel.Model.Base.Targets;
public partial class ExternalTarget public class ExternalTarget
{ {
[Key] [Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)] [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
@@ -24,5 +24,5 @@ public partial class ExternalTarget
[Column(Order = 4)] [Column(Order = 4)]
[Newtonsoft.Json.JsonIgnore] [Newtonsoft.Json.JsonIgnore]
public virtual ICollection<EntityExternal> EntityExternals { get; set; } = []; public ICollection<EntityExternal> EntityExternals { get; set; } = [];
} }
+1 -1
View File
@@ -3,7 +3,7 @@ using System.ComponentModel.DataAnnotations.Schema;
namespace Pf2eModel.Model.Common; namespace Pf2eModel.Model.Common;
public partial class Frequency public class Frequency
{ {
[Key] [Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)] [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
@@ -18,19 +18,21 @@ public static class LocaleExtension
public static LocaleType GetTypeByString(this string localeType) public static LocaleType GetTypeByString(this string localeType)
{ {
foreach (System.Reflection.FieldInfo field in typeof(LocaleType).GetFields()) foreach (FieldInfo field in typeof(LocaleType).GetFields())
{ {
if ( if (
Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute))
is DescriptionAttribute descriptionAttribute is not DescriptionAttribute descriptionAttribute
) )
{ {
continue;
}
if (descriptionAttribute.Description == localeType) if (descriptionAttribute.Description == localeType)
{ {
return (LocaleType)field.GetValue(null)!; return (LocaleType)field.GetValue(null)!;
} }
} }
}
throw new InvalidEnumArgumentException(); throw new InvalidEnumArgumentException();
} }
+1 -1
View File
@@ -141,7 +141,7 @@ public class LocaleString : ICollection<LocaleItem>
foreach (KeyValuePair<string, JToken?> item in json) foreach (KeyValuePair<string, JToken?> item in json)
{ {
result[item.Key] = item.ToString(); result[item.Key] = item.Value?.ToString() ?? "";
} }
return result; return result;
+1 -1
View File
@@ -4,7 +4,7 @@ using Pf2eModel.Model.Enum;
namespace Pf2eModel.Model.Common; namespace Pf2eModel.Model.Common;
public partial class Training public class Training
{ {
[Key] [Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)] [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+2 -1
View File
@@ -5,7 +5,7 @@ using Pf2eModel.Model.Enum;
namespace Pf2eModel.Model.Structure; namespace Pf2eModel.Model.Structure;
[EntityOfType(EntityType.Action)] [EntityOfType(EntityType.Action)]
public partial class Action : IEntity public class Action : IEntity
{ {
[System.ComponentModel.DataAnnotations.Key] [System.ComponentModel.DataAnnotations.Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)] [DatabaseGenerated(DatabaseGeneratedOption.None)]
@@ -28,6 +28,7 @@ public partial class Action : IEntity
public bool IsPassive { get; set; } = false; public bool IsPassive { get; set; } = false;
[Column(Order = 6)] [Column(Order = 6)]
// ReSharper disable once EntityFramework.ModelValidation.UnlimitedStringLength
public string? Extra { get; set; } public string? Extra { get; set; }
public Entity Entity { get; set; } = null!; public Entity Entity { get; set; } = null!;
+5 -5
View File
@@ -6,7 +6,7 @@ using Pf2eModel.Model.Structure.AncestryRelation;
namespace Pf2eModel.Model.Structure; namespace Pf2eModel.Model.Structure;
[EntityOfType(EntityType.Ancestry)] [EntityOfType(EntityType.Ancestry)]
public partial class Ancestry : IEntity public class Ancestry : IEntity
{ {
[System.ComponentModel.DataAnnotations.Key] [System.ComponentModel.DataAnnotations.Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)] [DatabaseGenerated(DatabaseGeneratedOption.None)]
@@ -27,11 +27,11 @@ public partial class Ancestry : IEntity
[Column(Order = 5)] [Column(Order = 5)]
public AncestryVision Vision { get; set; } = AncestryVision.Normal; public AncestryVision Vision { get; set; } = AncestryVision.Normal;
public virtual ICollection<AncestryBoost> AncestryBoosts { get; set; } = []; public ICollection<AncestryBoost> AncestryBoosts { get; set; } = [];
public virtual ICollection<AncestryItem> AncestryItems { get; set; } = []; public ICollection<AncestryItem> AncestryItems { get; set; } = [];
public virtual ICollection<AncestryLanguage> AncestryLanguages { get; set; } = []; public ICollection<AncestryLanguage> AncestryLanguages { get; set; } = [];
public virtual ICollection<Heritage> Heritages { get; set; } = []; public ICollection<Heritage> Heritages { get; set; } = [];
} }
@@ -4,7 +4,7 @@ using Newtonsoft.Json;
namespace Pf2eModel.Model.Structure.AncestryRelation; namespace Pf2eModel.Model.Structure.AncestryRelation;
public partial class AncestryBoost public class AncestryBoost
{ {
[Key] [Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)] [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
@@ -21,5 +21,5 @@ public partial class AncestryBoost
public Enum.AttributeValues Value { get; set; } public Enum.AttributeValues Value { get; set; }
[JsonIgnore] [JsonIgnore]
public virtual Ancestry Ancestry { get; set; } = null!; public Ancestry Ancestry { get; set; } = null!;
} }
@@ -5,7 +5,7 @@ using Pf2eModel.Model.Base;
namespace Pf2eModel.Model.Structure.AncestryRelation; namespace Pf2eModel.Model.Structure.AncestryRelation;
[PrimaryKey(nameof(AncestryId), nameof(ItemId))] [PrimaryKey(nameof(AncestryId), nameof(ItemId))]
public partial class AncestryItem public class AncestryItem
{ {
[Column(Order = 1)] [Column(Order = 1)]
public int AncestryId { get; set; } public int AncestryId { get; set; }
@@ -16,7 +16,7 @@ public partial class AncestryItem
[Column(Order = 3)] [Column(Order = 3)]
public int Level { get; set; } public int Level { get; set; }
public virtual Ancestry Ancestry { get; set; } = null!; public Ancestry Ancestry { get; set; } = null!;
public virtual Entity Item { get; set; } = null!; public Entity Item { get; set; } = null!;
} }
@@ -4,7 +4,7 @@ using Microsoft.EntityFrameworkCore;
namespace Pf2eModel.Model.Structure.AncestryRelation; namespace Pf2eModel.Model.Structure.AncestryRelation;
[PrimaryKey(nameof(AncestryId), nameof(LanguageId))] [PrimaryKey(nameof(AncestryId), nameof(LanguageId))]
public partial class AncestryLanguage public class AncestryLanguage
{ {
[Column(Order = 1)] [Column(Order = 1)]
public int AncestryId { get; set; } public int AncestryId { get; set; }
@@ -15,7 +15,7 @@ public partial class AncestryLanguage
[Column(Order = 3)] [Column(Order = 3)]
public bool IsAdditional { get; set; } public bool IsAdditional { get; set; }
public virtual Ancestry Ancestry { get; set; } = null!; public Ancestry Ancestry { get; set; } = null!;
public virtual Language Language { get; set; } = null!; public Language Language { get; set; } = null!;
} }
+2 -1
View File
@@ -21,9 +21,10 @@ public class Archetype : IEntity
public int? DedicationFeatId { get; set; } = null; public int? DedicationFeatId { get; set; } = null;
[Column(Order = 4)] [Column(Order = 4)]
// ReSharper disable once EntityFramework.ModelValidation.UnlimitedStringLength
public string? Extra { get; set; } public string? Extra { get; set; }
public Feat DedicationFeat { get; set; } = null!; public Feat DedicationFeat { get; set; } = null!;
public virtual ICollection<Feat> Feats { get; set; } = []; public ICollection<Feat> Feats { get; set; } = [];
} }
+4 -4
View File
@@ -7,7 +7,7 @@ using Pf2eModel.Model.Structure.BackgroundRelation;
namespace Pf2eModel.Model.Structure; namespace Pf2eModel.Model.Structure;
[EntityOfType(EntityType.Background)] [EntityOfType(EntityType.Background)]
public partial class Background : IEntity public class Background : IEntity
{ {
[Key] [Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)] [DatabaseGenerated(DatabaseGeneratedOption.None)]
@@ -16,9 +16,9 @@ public partial class Background : IEntity
public Entity Entity { get; set; } = null!; public Entity Entity { get; set; } = null!;
public virtual ICollection<BackgroundBoost> BackgroundBoosts { get; set; } = []; public ICollection<BackgroundBoost> BackgroundBoosts { get; set; } = [];
public virtual ICollection<BackgroundItem> BackgroundItems { get; set; } = []; public ICollection<BackgroundItem> BackgroundItems { get; set; } = [];
public virtual ICollection<BackgroundTraining> BackgroundSkills { get; set; } = []; public ICollection<BackgroundTraining> BackgroundSkills { get; set; } = [];
} }
@@ -5,7 +5,7 @@ using Pf2eModel.Model.Enum;
namespace Pf2eModel.Model.Structure.BackgroundRelation; namespace Pf2eModel.Model.Structure.BackgroundRelation;
public partial class BackgroundBoost public class BackgroundBoost
{ {
[Key] [Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)] [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
@@ -19,5 +19,5 @@ public partial class BackgroundBoost
public AttributeValues Attribute { get; set; } public AttributeValues Attribute { get; set; }
[JsonIgnore] [JsonIgnore]
public virtual Background Background { get; set; } = null!; public Background Background { get; set; } = null!;
} }
@@ -6,7 +6,7 @@ using Pf2eModel.Model.Base;
namespace Pf2eModel.Model.Structure.BackgroundRelation; namespace Pf2eModel.Model.Structure.BackgroundRelation;
[PrimaryKey(nameof(BackgroundId), nameof(EntityId))] [PrimaryKey(nameof(BackgroundId), nameof(EntityId))]
public partial class BackgroundItem public class BackgroundItem
{ {
[Column(Order = 1)] [Column(Order = 1)]
public int BackgroundId { get; set; } public int BackgroundId { get; set; }
@@ -18,7 +18,7 @@ public partial class BackgroundItem
public int? Level { get; set; } public int? Level { get; set; }
[JsonIgnore] [JsonIgnore]
public virtual Background Background { get; set; } = null!; public Background Background { get; set; } = null!;
public virtual Entity Entity { get; set; } = null!; public Entity Entity { get; set; } = null!;
} }
@@ -6,7 +6,7 @@ using Pf2eModel.Model.Enum;
namespace Pf2eModel.Model.Structure.BackgroundRelation; namespace Pf2eModel.Model.Structure.BackgroundRelation;
public partial class BackgroundTraining public class BackgroundTraining
{ {
[Key] [Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)] [DatabaseGenerated(DatabaseGeneratedOption.None)]
@@ -23,7 +23,7 @@ public partial class BackgroundTraining
public TrainingStage Level { get; set; } public TrainingStage Level { get; set; }
[JsonIgnore] [JsonIgnore]
public virtual Background Background { get; set; } = null!; public Background Background { get; set; } = null!;
public virtual Training Training { get; set; } = null!; public Training Training { get; set; } = null!;
} }
+1 -1
View File
@@ -7,7 +7,7 @@ using Pf2eModel.Model.Structure.ClassRelation;
namespace Pf2eModel.Model.Structure; namespace Pf2eModel.Model.Structure;
[EntityOfType(EntityType.Class)] [EntityOfType(EntityType.Class)]
public partial class Class : IEntity public class Class : IEntity
{ {
[Key] [Key]
[Column(Order = 1)] [Column(Order = 1)]
@@ -5,7 +5,7 @@ using Pf2eModel.Model.Enum;
namespace Pf2eModel.Model.Structure.ClassRelation; namespace Pf2eModel.Model.Structure.ClassRelation;
public partial class ClassKeyAbility public class ClassKeyAbility
{ {
[Key] [Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)] [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
@@ -19,5 +19,5 @@ public partial class ClassKeyAbility
public AttributeValues Value { get; set; } public AttributeValues Value { get; set; }
[JsonIgnore] [JsonIgnore]
public virtual Class Class { get; set; } = null!; public Class Class { get; set; } = null!;
} }
@@ -6,7 +6,7 @@ using Pf2eModel.Model.Enum;
namespace Pf2eModel.Model.Structure.ClassRelation; namespace Pf2eModel.Model.Structure.ClassRelation;
public partial class ClassProgression public class ClassProgression
{ {
[Key] [Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)] [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
@@ -26,7 +26,7 @@ public partial class ClassProgression
public int? EntityId { get; set; } public int? EntityId { get; set; }
[JsonIgnore] [JsonIgnore]
public virtual Class Class { get; set; } = null!; public Class Class { get; set; } = null!;
public virtual Entity? Entity { get; set; } public Entity? Entity { get; set; }
} }
@@ -6,7 +6,7 @@ using Pf2eModel.Model.Enum;
namespace Pf2eModel.Model.Structure.ClassRelation; namespace Pf2eModel.Model.Structure.ClassRelation;
public partial class ClassTraining public class ClassTraining
{ {
[Key] [Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)] [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
@@ -27,7 +27,7 @@ public partial class ClassTraining
public TrainingStage Stage { get; set; } public TrainingStage Stage { get; set; }
[JsonIgnore] [JsonIgnore]
public virtual Class Class { get; set; } = null!; public Class Class { get; set; } = null!;
public virtual Training? Training { get; set; } public Training? Training { get; set; }
} }
+2 -1
View File
@@ -6,7 +6,7 @@ using Pf2eModel.Model.Enum;
namespace Pf2eModel.Model.Structure; namespace Pf2eModel.Model.Structure;
[EntityOfType(EntityType.Condition)] [EntityOfType(EntityType.Condition)]
public partial class Condition : IEntity public class Condition : IEntity
{ {
[Key] [Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)] [DatabaseGenerated(DatabaseGeneratedOption.None)]
@@ -19,5 +19,6 @@ public partial class Condition : IEntity
public ConditionGroup? Group { get; set; } public ConditionGroup? Group { get; set; }
[Column(Order = 3)] [Column(Order = 3)]
// ReSharper disable once EntityFramework.ModelValidation.UnlimitedStringLength
public string? Extra { get; set; } public string? Extra { get; set; }
} }
+6 -7
View File
@@ -8,7 +8,7 @@ using Pf2eModel.Model.Structure.DeityRelation;
namespace Pf2eModel.Model.Structure; namespace Pf2eModel.Model.Structure;
[EntityOfType(EntityType.Deity)] [EntityOfType(EntityType.Deity)]
public partial class Deity : IEntity public class Deity : IEntity
{ {
[Key] [Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)] [DatabaseGenerated(DatabaseGeneratedOption.None)]
@@ -29,14 +29,13 @@ public partial class Deity : IEntity
[Column(Order = 5)] [Column(Order = 5)]
public bool SanctificationMust { get; set; } public bool SanctificationMust { get; set; }
public virtual ICollection<DeityAttribute> DeityAttributes { get; set; } = public ICollection<DeityAttribute> DeityAttributes { get; set; } = new List<DeityAttribute>();
new List<DeityAttribute>();
public virtual ICollection<DeityDomain> DeityDomains { get; set; } = new List<DeityDomain>(); public ICollection<DeityDomain> DeityDomains { get; set; } = new List<DeityDomain>();
public virtual ICollection<DeitySpell> DeitySpells { get; set; } = new List<DeitySpell>(); public ICollection<DeitySpell> DeitySpells { get; set; } = new List<DeitySpell>();
public virtual ICollection<DeityWeapon> DeityWeapons { get; set; } = new List<DeityWeapon>(); public ICollection<DeityWeapon> DeityWeapons { get; set; } = new List<DeityWeapon>();
public virtual ICollection<Training> Skills { get; set; } = new List<Training>(); public ICollection<Training> Skills { get; set; } = new List<Training>();
} }
@@ -5,7 +5,7 @@ using Pf2eModel.Model.Enum;
namespace Pf2eModel.Model.Structure.DeityRelation; namespace Pf2eModel.Model.Structure.DeityRelation;
[PrimaryKey(nameof(DeityId), nameof(Attribute))] [PrimaryKey(nameof(DeityId), nameof(Attribute))]
public partial class DeityAttribute public class DeityAttribute
{ {
[Column(Order = 1)] [Column(Order = 1)]
public int DeityId { get; set; } public int DeityId { get; set; }
@@ -13,5 +13,5 @@ public partial class DeityAttribute
[Column(Order = 2)] [Column(Order = 2)]
public AttributeValues Attribute { get; set; } public AttributeValues Attribute { get; set; }
public virtual Deity Deity { get; set; } = null!; public Deity Deity { get; set; } = null!;
} }
@@ -3,7 +3,7 @@ using System.ComponentModel.DataAnnotations.Schema;
namespace Pf2eModel.Model.Structure.DeityRelation; namespace Pf2eModel.Model.Structure.DeityRelation;
public partial class DeityDomain public class DeityDomain
{ {
[Key] [Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)] [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
@@ -3,7 +3,7 @@ using System.ComponentModel.DataAnnotations.Schema;
namespace Pf2eModel.Model.Structure.DeityRelation; namespace Pf2eModel.Model.Structure.DeityRelation;
public partial class DeitySpell public class DeitySpell
{ {
[Key] [Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)] [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
@@ -3,7 +3,7 @@ using System.ComponentModel.DataAnnotations.Schema;
namespace Pf2eModel.Model.Structure.DeityRelation; namespace Pf2eModel.Model.Structure.DeityRelation;
public partial class DeityWeapon public class DeityWeapon
{ {
[Key] [Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)] [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+4 -3
View File
@@ -7,7 +7,7 @@ using Pf2eModel.Model.Structure.FeatRelation;
namespace Pf2eModel.Model.Structure; namespace Pf2eModel.Model.Structure;
[EntityOfType(EntityType.Feat)] [EntityOfType(EntityType.Feat)]
public partial class Feat : IEntity public class Feat : IEntity
{ {
[Key] [Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)] [DatabaseGenerated(DatabaseGeneratedOption.None)]
@@ -38,9 +38,10 @@ public partial class Feat : IEntity
public bool IsPassive { get; set; } = false; public bool IsPassive { get; set; } = false;
[Column(Order = 10)] [Column(Order = 10)]
// ReSharper disable once EntityFramework.ModelValidation.UnlimitedStringLength
public string? Extra { get; set; } public string? Extra { get; set; }
public virtual ICollection<Archetype> Archetypes { get; set; } = []; public ICollection<Archetype> Archetypes { get; set; } = [];
public virtual ICollection<FeatPrerequisite> FeatPrerequisites { get; set; } = []; public ICollection<FeatPrerequisite> FeatPrerequisites { get; set; } = [];
} }
@@ -3,7 +3,7 @@ using System.ComponentModel.DataAnnotations.Schema;
namespace Pf2eModel.Model.Structure.FeatRelation; namespace Pf2eModel.Model.Structure.FeatRelation;
public partial class FeatPrerequisite public class FeatPrerequisite
{ {
[Key] [Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)] [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+1 -1
View File
@@ -6,7 +6,7 @@ using Pf2eModel.Model.Enum;
namespace Pf2eModel.Model.Structure; namespace Pf2eModel.Model.Structure;
[EntityOfType(EntityType.Heritage)] [EntityOfType(EntityType.Heritage)]
public partial class Heritage : IEntity public class Heritage : IEntity
{ {
[Key] [Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)] [DatabaseGenerated(DatabaseGeneratedOption.None)]
+1 -1
View File
@@ -6,7 +6,7 @@ using Pf2eModel.Model.Enum;
namespace Pf2eModel.Model.Structure; namespace Pf2eModel.Model.Structure;
[EntityOfType(EntityType.Language)] [EntityOfType(EntityType.Language)]
public partial class Language : IEntity public class Language : IEntity
{ {
[Key] [Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)] [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+1 -1
View File
@@ -7,7 +7,7 @@ using Pf2eModel.Model.Structure.SpellRelation;
namespace Pf2eModel.Model.Structure; namespace Pf2eModel.Model.Structure;
[EntityOfType(EntityType.Spell)] [EntityOfType(EntityType.Spell)]
public partial class Spell : IEntity public class Spell : IEntity
{ {
[Key] [Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)] [DatabaseGenerated(DatabaseGeneratedOption.None)]
@@ -4,7 +4,7 @@ using Pf2eModel.Model.Enum;
namespace Pf2eModel.Model.Structure.SpellRelation; namespace Pf2eModel.Model.Structure.SpellRelation;
public partial class SpellArea public class SpellArea
{ {
[Key] [Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)] [DatabaseGenerated(DatabaseGeneratedOption.None)]
@@ -4,7 +4,7 @@ using Pf2eModel.Model.Enum;
namespace Pf2eModel.Model.Structure.SpellRelation; namespace Pf2eModel.Model.Structure.SpellRelation;
public partial class SpellDamage public class SpellDamage
{ {
[Key] [Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)] [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
@@ -3,7 +3,7 @@ using System.ComponentModel.DataAnnotations.Schema;
namespace Pf2eModel.Model.Structure.SpellRelation; namespace Pf2eModel.Model.Structure.SpellRelation;
public partial class SpellDefense public class SpellDefense
{ {
[Key] [Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)] [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
@@ -3,7 +3,7 @@ using System.ComponentModel.DataAnnotations.Schema;
namespace Pf2eModel.Model.Structure.SpellRelation; namespace Pf2eModel.Model.Structure.SpellRelation;
public partial class SpellRitual public class SpellRitual
{ {
[Key] [Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)] [DatabaseGenerated(DatabaseGeneratedOption.None)]
+3 -1
View File
@@ -21,7 +21,9 @@ public class Subclass : IEntity
public SubclassGroup Group { get; set; } = null!; public SubclassGroup Group { get; set; } = null!;
[Column(Order = 3)] [Column(Order = 3)]
public LocaleString Additional { get; set; } = new(); public LocaleString Additional { get; set; } = [];
[Column(Order = 4)]
// ReSharper disable once EntityFramework.ModelValidation.UnlimitedStringLength
public string Extra { get; set; } = "{}"; public string Extra { get; set; } = "{}";
} }
+2 -8
View File
@@ -1,13 +1,7 @@
using System; using System.ComponentModel.DataAnnotations;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Pf2eModel.Model.Base; using Pf2eModel.Model.Base;
using Pf2eModel.Model.Enum; using Pf2eModel.Model.Enum;
using Pf2eModel.Model.Structure.ClassRelation;
namespace Pf2eModel.Model.Structure; namespace Pf2eModel.Model.Structure;
@@ -25,5 +19,5 @@ public class SubclassGroup : IEntity
public Class? Class { get; set; } = null!; public Class? Class { get; set; } = null!;
public virtual ICollection<Subclass> Subclasses { get; set; } = []; public ICollection<Subclass> Subclasses { get; set; } = [];
} }
+1 -1
View File
@@ -6,7 +6,7 @@ using Pf2eModel.Model.Enum;
namespace Pf2eModel.Model.Structure; namespace Pf2eModel.Model.Structure;
[EntityOfType(EntityType.Trait)] [EntityOfType(EntityType.Trait)]
public partial class Trait : IEntity public class Trait : IEntity
{ {
[Key] [Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)] [DatabaseGenerated(DatabaseGeneratedOption.None)]
+8 -6
View File
@@ -1,4 +1,5 @@
using System.Linq.Expressions; using System.Diagnostics.CodeAnalysis;
using System.Linq.Expressions;
using System.Reflection; using System.Reflection;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders; using Microsoft.EntityFrameworkCore.Metadata.Builders;
@@ -21,6 +22,7 @@ using Pf2eModel.Model.Structure.SpellRelation;
namespace Pf2eModel; namespace Pf2eModel;
[SuppressMessage("ReSharper", "UnusedParameter.Local")]
public partial class Pf2eDbContext public partial class Pf2eDbContext
{ {
public const int NameLength = 255; public const int NameLength = 255;
@@ -39,11 +41,7 @@ public partial class Pf2eDbContext
private void BindEntity<T>(EntityTypeBuilder<T> entity) private void BindEntity<T>(EntityTypeBuilder<T> entity)
where T : class, IEntity where T : class, IEntity
{ {
entity entity.HasOne(e => e.Entity).WithOne().HasForeignKey<T>(e => e.Id).IsRequired();
.HasOne(entity => entity.Entity)
.WithOne()
.HasForeignKey<T>(entity => entity.Id)
.IsRequired();
} }
private void BindLocaleString<T>( private void BindLocaleString<T>(
@@ -144,6 +142,8 @@ public partial class Pf2eDbContext
// Keywords // Keywords
modelBuilder.Entity<Keyword>(entity => modelBuilder.Entity<Keyword>(entity =>
{ {
BindLocaleString(entity, k => k.Name);
entity entity
.HasMany(e => e.Aliases) .HasMany(e => e.Aliases)
.WithOne(e => e.Keyword) .WithOne(e => e.Keyword)
@@ -483,9 +483,11 @@ public partial class Pf2eDbContext
); );
} }
// ReSharper disable once InconsistentNaming
public static bool LocaleSearch(LocaleString data, string search_value) => public static bool LocaleSearch(LocaleString data, string search_value) =>
throw new InvalidOperationException("Only for LINQ translation"); throw new InvalidOperationException("Only for LINQ translation");
// ReSharper disable once InconsistentNaming
public static bool LocaleSearchArray(LocaleString data, IEnumerable<string> search_values) => public static bool LocaleSearchArray(LocaleString data, IEnumerable<string> search_values) =>
throw new InvalidOperationException("Only for LINQ translation"); throw new InvalidOperationException("Only for LINQ translation");
} }
+130
View File
@@ -0,0 +1,130 @@
using System.ComponentModel;
using Newtonsoft.Json;
using Pf2eModel;
using Pf2eModel.Model;
using Pf2eModel.Model.Base;
using Pf2eModel.Model.Common.I18n;
using Pf2eModel.Model.Structure;
namespace Pf2eViewer.Converters;
internal class EntityConverter : JsonConverter
{
private readonly Pf2eDbContext _context = new();
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Entity) || objectType.IsAssignableTo(typeof(IEntity));
}
private Dictionary<string, object?> DictionarySerialization(object source)
{
Dictionary<string, object?> dictionary = [];
foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(source))
{
if (property.Attributes.OfType<JsonIgnoreAttribute>().Any())
{
continue;
}
object? v = property.GetValue(source);
dictionary.Add(property.Name, v);
}
return dictionary;
}
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
{
if (value == null)
{
serializer.Serialize(writer, null);
return;
}
Dictionary<string, object?> dictionary = DictionarySerialization(value);
if (value.GetType() != typeof(Entity))
{
dictionary.Remove("Entity");
Processor? processor = _processors.FirstOrDefault(p => p.Type == value.GetType());
processor?.Process(dictionary, (IEntity)value, this);
}
else
{
dictionary.Remove("Description");
}
serializer.Serialize(writer, dictionary);
}
private abstract class Processor
{
public abstract Type Type { get; }
public abstract void Process(
Dictionary<string, object?> dictionary,
IEntity entity,
EntityConverter converter
);
}
private class Processor<T>(Action<Dictionary<string, object?>, T, EntityConverter> method)
: Processor
where T : IEntity
{
public override Type Type { get; } = typeof(T);
public override void Process(
Dictionary<string, object?> dictionary,
IEntity entity,
EntityConverter converter
)
{
method.Invoke(dictionary, (T)entity, converter);
}
}
private readonly ICollection<Processor> _processors =
[
new Processor<Trait>(
(dictionary, trait, converter) =>
{
LocaleString name =
// ReSharper disable once ConditionalAccessQualifierIsNonNullableAccordingToAPIContract
trait.Entity?.Name
?? converter
._context.Entities.Where(e => e.Id == trait.Id)
.Select(e => e.Name)
.FirstOrDefault()!;
dictionary.Remove("Entity");
dictionary.Remove("Entities");
dictionary.Add("Name", name.ToString());
}
),
new Processor<Subclass>(
(dictionary, _, _) =>
{
dictionary.Remove("Group");
}
),
];
public override bool CanRead => false;
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."
);
}
}
@@ -0,0 +1,41 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Pf2eModel.Model.Common.I18n;
namespace Pf2eViewer.Converters;
public class LocaleStringConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(LocaleString);
}
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
{
JObject result = new();
LocaleString str = (LocaleString)value!;
foreach (LocaleItem item in str)
{
result.Add(item.Locale.GetTypeString(), item.Value);
}
result.WriteTo(writer);
}
public override bool CanRead => false;
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."
);
}
}
+19
View File
@@ -0,0 +1,19 @@
namespace Pf2eViewer;
internal sealed class CoreBindingNavigator : BindingNavigator
{
public CoreBindingNavigator()
{
AddStandardItems();
if (DeleteItem != null)
{
DeleteItem.Visible = false;
}
if (AddNewItem != null)
{
AddNewItem.Visible = false;
}
}
}
File diff suppressed because it is too large Load Diff
+110
View File
@@ -0,0 +1,110 @@
namespace Pf2eViewer;
partial class DatabaseViewerControl
{
/// <summary>
/// Обязательная переменная конструктора.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Освободить все используемые ресурсы.
/// </summary>
/// <param name="disposing">истинно, если управляемый ресурс должен быть удален; иначе ложно.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Код, автоматически созданный конструктором компонентов
/// <summary>
/// Требуемый метод для поддержки конструктора — не изменяйте
/// содержимое этого метода с помощью редактора кода.
/// </summary>
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
BindingNavigator = new CoreBindingNavigator();
SearchTextBox = new TextBox();
DataGridView = new DataGridView();
DbBindingSource = new BindingSource(components);
PagesBindingSource = new BindingSource(components);
( BindingNavigator).BeginInit();
((System.ComponentModel.ISupportInitialize) DataGridView).BeginInit();
((System.ComponentModel.ISupportInitialize) DbBindingSource).BeginInit();
((System.ComponentModel.ISupportInitialize) PagesBindingSource).BeginInit();
SuspendLayout();
//
// BindingNavigator
//
BindingNavigator.AddNewItem = null;
BindingNavigator.DeleteItem = null;
BindingNavigator.ImeMode = ImeMode.NoControl;
BindingNavigator.LayoutStyle = ToolStripLayoutStyle.HorizontalStackWithOverflow;
BindingNavigator.Location = new Point(0, 0);
BindingNavigator.Name = "BindingNavigator";
BindingNavigator.Size = new Size(345, 25);
BindingNavigator.Stretch = true;
BindingNavigator.TabIndex = 1;
BindingNavigator.Text = "coreBindingNavigator1";
BindingNavigator.RefreshItems += BindingNavigator_RefreshItems;
//
// SearchTextBox
//
SearchTextBox.Dock = DockStyle.Top;
SearchTextBox.Location = new Point(0, 25);
SearchTextBox.Name = "SearchTextBox";
SearchTextBox.PlaceholderText = "Поиск";
SearchTextBox.Size = new Size(345, 23);
SearchTextBox.TabIndex = 3;
//
// DataGridView
//
DataGridView.AllowUserToAddRows = false;
DataGridView.AllowUserToDeleteRows = false;
DataGridView.AutoGenerateColumns = false;
DataGridView.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCellsExceptHeader;
DataGridView.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.AllCellsExceptHeaders;
DataGridView.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize;
DataGridView.DataSource = DbBindingSource;
DataGridView.Dock = DockStyle.Fill;
DataGridView.Location = new Point(0, 48);
DataGridView.MultiSelect = false;
DataGridView.Name = "DataGridView";
DataGridView.ReadOnly = true;
DataGridView.RowHeadersVisible = false;
DataGridView.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
DataGridView.Size = new Size(345, 345);
DataGridView.TabIndex = 4;
DataGridView.RowStateChanged += DataGridView_RowStateChanged;
//
// DatabaseViewerControl
//
AutoScaleDimensions = new SizeF(7F, 15F);
AutoScaleMode = AutoScaleMode.Font;
Controls.Add(DataGridView);
Controls.Add(SearchTextBox);
Controls.Add(BindingNavigator);
Name = "DatabaseViewerControl";
Size = new Size(345, 393);
Load += DatabaseViewerControl_Load;
( BindingNavigator).EndInit();
((System.ComponentModel.ISupportInitialize) DataGridView).EndInit();
((System.ComponentModel.ISupportInitialize) DbBindingSource).EndInit();
((System.ComponentModel.ISupportInitialize) PagesBindingSource).EndInit();
ResumeLayout(false);
PerformLayout();
}
#endregion
private CoreBindingNavigator BindingNavigator;
private TextBox SearchTextBox;
private DataGridView DataGridView;
private BindingSource DbBindingSource;
private BindingSource PagesBindingSource;
}
+143
View File
@@ -0,0 +1,143 @@
using Pf2eModel;
using Pf2eModel.Model.Base;
using Pf2eModel.Model.Common.I18n;
using Pf2eModel.Model.Enum;
namespace Pf2eViewer;
public partial class DatabaseViewerControl : UserControl
{
public readonly Pf2eDbContext Context = new();
private int _perPage = 30;
private int _page = 1;
private EntityType _type;
public int PerPage
{
get => _perPage;
set
{
_perPage = value;
UpdateView();
}
}
public int Page
{
get => _page;
set
{
_page = value;
UpdateView();
}
}
public int Count { get; private set; }
public int MaxPage => Convert.ToInt32(Math.Ceiling(1.0 * Count / PerPage));
public EntityView CurrentEntity { get; private set; } = null!;
public EntityType Type
{
get => _type;
set
{
_type = value;
UpdateView();
}
}
internal bool _updating = false;
public DatabaseViewerControl()
{
InitializeComponent();
BindingNavigator.BindingSource = DbBindingSource;
DataGridView.AutoGenerateColumns = true;
DataGridView.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
}
private void DatabaseViewerControl_Load(object sender, EventArgs e)
{
UpdateView();
}
private void BindingNavigator_RefreshItems(object sender, EventArgs e)
{
if (_updating)
{
return;
}
Page = (int)(BindingNavigator?.BindingSource?.Current ?? 1);
UpdateView();
}
public void UpdateView()
{
_updating = true;
IQueryable<Entity> query = Context.Entities.Where(e =>
e.Type == Type
&& (
// ReSharper disable once EntityFramework.UnsupportedServerSideFunctionCall
SearchTextBox.Text == ""
|| Pf2eDbContext.LocaleSearch(e.Name, SearchTextBox.Text)
)
);
Count = query.Count();
PagesBindingSource.DataSource = Enumerable.Range(1, MaxPage).ToList();
BindingNavigator.BindingSource = PagesBindingSource;
DbBindingSource.DataSource = query
.Skip((Page - 1) * PerPage)
.Take(PerPage)
.Select(e => new EntityGridRow(e))
.ToList();
if (DataGridView.Rows.Count > 0)
{
DataGridView.Rows[0].Selected = true;
}
DataGridView.Update();
_updating = false;
}
private void DataGridView_RowStateChanged(object sender, DataGridViewRowStateChangedEventArgs e)
{
if (DataGridView.SelectedRows.Count == 0)
{
return;
}
EntityGridRow data = (EntityGridRow)DataGridView.SelectedRows[0].DataBoundItem;
CurrentEntity = new(Context, Type, data.Id);
EntityChange?.Invoke(CurrentEntity);
}
public delegate void EntityChangeHandler(EntityView newEntity);
public event EntityChangeHandler? EntityChange;
internal class EntityGridRow(Entity e)
{
public int Id { get; set; } = e.Id;
public string Name { get; set; } = e.Name[LocaleType.English];
public EntityRarity Rarity { get; set; } = e.Rarity;
private readonly Entity _e = e;
public Entity GetEntity() => _e;
}
}
+126
View File
@@ -0,0 +1,126 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="DbBindingSource.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
<metadata name="PagesBindingSource.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>163, 17</value>
</metadata>
</root>
-99
View File
@@ -1,99 +0,0 @@
using System.ComponentModel;
using Newtonsoft.Json;
using Pf2eModel;
using Pf2eModel.Model;
using Pf2eModel.Model.Base;
using Pf2eModel.Model.Common.I18n;
using Pf2eModel.Model.Structure;
namespace Pf2eViewer;
internal class EntityConverter : JsonConverter
{
private readonly Pf2eDbContext _context = new();
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Entity) || objectType.IsAssignableTo(typeof(IEntity));
}
private Dictionary<string, object?> DictionarySerialization(object source)
{
Dictionary<string, object?> dictionary = [];
foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(source))
{
if (property.Attributes.OfType<JsonIgnoreAttribute>().Any())
{
continue;
}
object? v = property.GetValue(source);
dictionary.Add(property.Name, v);
}
return dictionary;
}
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
{
Dictionary<string, object?> dictionary = DictionarySerialization(value!);
/*
if (value.GetType() != typeof(Entity))
{
dictionary.Remove("Entity");
}
*/
switch (value)
{
case Entity entity:
ProcessEntity(dictionary, entity);
break;
case Trait trait:
ProcessTrait(dictionary, trait);
break;
}
writer.WriteStartObject();
foreach (KeyValuePair<string, object> keyValuePair in dictionary)
{
writer.WritePropertyName(keyValuePair.Key, true);
serializer.Serialize(writer, keyValuePair.Value);
}
writer.WriteEndObject();
}
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()!;
dictionary.Remove("Entity");
dictionary.Remove("Entities");
dictionary.Add("Name", name.ToString());
}
private void ProcessEntity(Dictionary<string, object?> dictionary, Entity entity)
{
dictionary.Remove("Description");
}
public override bool CanRead => false;
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."
);
}
}
+96
View File
@@ -0,0 +1,96 @@
using Microsoft.EntityFrameworkCore;
using Pf2eModel;
using Pf2eModel.Model;
using Pf2eModel.Model.Base;
using Pf2eModel.Model.Enum;
using Pf2eModel.Model.Structure;
namespace Pf2eViewer;
public class EntityView
{
public Entity? Entity;
public IEntity? SubEntity;
public EntityView(Pf2eDbContext context, EntityType type, int id)
{
Getter? getter = _getters.FirstOrDefault(g => g.Type == type);
Entity fullEntity =
context
.Entities.Where(e => e.Id == id && e.Type == type)
.Include(e => e.Sources)
.ThenInclude(s => s.Source)
.Include(e => e.Keywords)
.Include(e => e.Externals)
.ThenInclude(x => x.Target)
.Include(e => e.Traits)
.ThenInclude(t => t.Entity)
.FirstOrDefault() ?? throw new Exception("Entity is null");
IEntity? subEntity =
(getter != null)
? getter.Get(id, context)
: (IEntity?)context.Find(EntityOfTypeAttribute.Resolve(fullEntity), id);
Entity = fullEntity;
SubEntity = subEntity;
}
private readonly ICollection<Getter> _getters =
[
new Getter<Ancestry>(
EntityType.Ancestry,
context => context.Ancestries.Include(a => a.AncestryBoosts)
),
new Getter<Archetype>(
EntityType.Archetype,
context =>
context
.Archetypes.Include(a => a.DedicationFeat)
.ThenInclude(f => f.Entity)
.Include(a => a.Feats)
.ThenInclude(f => f.Entity)
),
new Getter<Background>(
EntityType.Background,
context =>
context
.Backgrounds.Include(b => b.BackgroundBoosts)
.Include(b => b.BackgroundSkills)
.ThenInclude(t => t.Training)
.Include(b => b.BackgroundItems)
),
new Getter<Class>(
EntityType.Class,
context =>
context
.Classes.Include(c => c.ClassKeyAbilities)
.Include(c => c.ClassTrainings)
.ThenInclude(t => t.Training)
.Include(c => c.ClassProgressions)
),
new Getter<Feat>(
EntityType.Feat,
context =>
context.Feats.Include(f => f.Archetypes).ThenInclude(a => a.Entity).AsQueryable()
),
];
private abstract class Getter(EntityType type)
{
public readonly EntityType Type = type;
public abstract IEntity? Get(int id, Pf2eDbContext context);
}
private class Getter<T>(EntityType type, Func<Pf2eDbContext, IQueryable<T>> method)
: Getter(type)
where T : IEntity
{
public override IEntity? Get(int id, Pf2eDbContext context)
{
return method.Invoke(context).FirstOrDefault(c => c.Id == id);
}
}
}
+26 -127
View File
@@ -31,16 +31,9 @@ partial class MainViewForm
RootSplitContainer = new SplitContainer(); RootSplitContainer = new SplitContainer();
InnerSplitContainer = new SplitContainer(); InnerSplitContainer = new SplitContainer();
TypesListBox = new ListBox(); TypesListBox = new ListBox();
EntityGridView = new DataGridView(); DatabaseViewer = new DatabaseViewerControl();
PaginationPagel = new Panel();
PageTextBox = new TextBox();
PaginationLabel = new TextBox();
LastPageButton = new Button();
NextPageButton = new Button();
PrevPageButton = new Button();
FirstPageButton = new Button();
ResultSplitContainer = new SplitContainer(); ResultSplitContainer = new SplitContainer();
EntityTreeView = new TreeView(); JsonWebView = new Microsoft.Web.WebView2.WinForms.WebView2();
DescriptionTextBox = new TextBox(); DescriptionTextBox = new TextBox();
((System.ComponentModel.ISupportInitialize) RootSplitContainer).BeginInit(); ((System.ComponentModel.ISupportInitialize) RootSplitContainer).BeginInit();
RootSplitContainer.Panel1.SuspendLayout(); RootSplitContainer.Panel1.SuspendLayout();
@@ -50,12 +43,11 @@ partial class MainViewForm
InnerSplitContainer.Panel1.SuspendLayout(); InnerSplitContainer.Panel1.SuspendLayout();
InnerSplitContainer.Panel2.SuspendLayout(); InnerSplitContainer.Panel2.SuspendLayout();
InnerSplitContainer.SuspendLayout(); InnerSplitContainer.SuspendLayout();
((System.ComponentModel.ISupportInitialize) EntityGridView).BeginInit();
PaginationPagel.SuspendLayout();
((System.ComponentModel.ISupportInitialize) ResultSplitContainer).BeginInit(); ((System.ComponentModel.ISupportInitialize) ResultSplitContainer).BeginInit();
ResultSplitContainer.Panel1.SuspendLayout(); ResultSplitContainer.Panel1.SuspendLayout();
ResultSplitContainer.Panel2.SuspendLayout(); ResultSplitContainer.Panel2.SuspendLayout();
ResultSplitContainer.SuspendLayout(); ResultSplitContainer.SuspendLayout();
((System.ComponentModel.ISupportInitialize) JsonWebView).BeginInit();
SuspendLayout(); SuspendLayout();
// //
// RootSplitContainer // RootSplitContainer
@@ -90,8 +82,7 @@ partial class MainViewForm
// //
// InnerSplitContainer.Panel2 // InnerSplitContainer.Panel2
// //
InnerSplitContainer.Panel2.Controls.Add(EntityGridView); InnerSplitContainer.Panel2.Controls.Add(DatabaseViewer);
InnerSplitContainer.Panel2.Controls.Add(PaginationPagel);
InnerSplitContainer.Size = new Size(254, 688); InnerSplitContainer.Size = new Size(254, 688);
InnerSplitContainer.SplitterDistance = 102; InnerSplitContainer.SplitterDistance = 102;
InnerSplitContainer.TabIndex = 1; InnerSplitContainer.TabIndex = 1;
@@ -107,102 +98,13 @@ partial class MainViewForm
TypesListBox.TabIndex = 0; TypesListBox.TabIndex = 0;
TypesListBox.SelectedIndexChanged += TypesListBox_SelectedIndexChanged; TypesListBox.SelectedIndexChanged += TypesListBox_SelectedIndexChanged;
// //
// EntityGridView // EntityView
// //
EntityGridView.AllowUserToAddRows = false; DatabaseViewer.Dock = DockStyle.Fill;
EntityGridView.AllowUserToDeleteRows = false; DatabaseViewer.Location = new Point(0, 0);
EntityGridView.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells; DatabaseViewer.Name = "DatabaseViewer";
EntityGridView.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize; DatabaseViewer.Size = new Size(254, 582);
EntityGridView.Dock = DockStyle.Fill; DatabaseViewer.TabIndex = 0;
EntityGridView.Location = new Point(0, 53);
EntityGridView.MultiSelect = false;
EntityGridView.Name = "EntityGridView";
EntityGridView.ReadOnly = true;
EntityGridView.RowHeadersVisible = false;
EntityGridView.RowHeadersWidthSizeMode = DataGridViewRowHeadersWidthSizeMode.AutoSizeToDisplayedHeaders;
EntityGridView.ShowEditingIcon = false;
EntityGridView.Size = new Size(254, 529);
EntityGridView.TabIndex = 0;
EntityGridView.RowStateChanged += EntityGridView_RowStateChanged;
//
// PaginationPagel
//
PaginationPagel.Controls.Add(PageTextBox);
PaginationPagel.Controls.Add(PaginationLabel);
PaginationPagel.Controls.Add(LastPageButton);
PaginationPagel.Controls.Add(NextPageButton);
PaginationPagel.Controls.Add(PrevPageButton);
PaginationPagel.Controls.Add(FirstPageButton);
PaginationPagel.Dock = DockStyle.Top;
PaginationPagel.Location = new Point(0, 0);
PaginationPagel.Name = "PaginationPagel";
PaginationPagel.Size = new Size(254, 53);
PaginationPagel.TabIndex = 1;
//
// PageTextBox
//
PageTextBox.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right;
PageTextBox.Location = new Point(81, 4);
PageTextBox.Name = "PageTextBox";
PageTextBox.Size = new Size(92, 23);
PageTextBox.TabIndex = 8;
PageTextBox.Text = "1";
PageTextBox.TextAlign = HorizontalAlignment.Center;
PageTextBox.KeyPress += PageTextBox_KeyPress;
//
// PaginationLabel
//
PaginationLabel.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right;
PaginationLabel.BorderStyle = BorderStyle.None;
PaginationLabel.Location = new Point(3, 31);
PaginationLabel.Name = "PaginationLabel";
PaginationLabel.ReadOnly = true;
PaginationLabel.Size = new Size(248, 16);
PaginationLabel.TabIndex = 7;
PaginationLabel.Text = "Всего";
PaginationLabel.TextAlign = HorizontalAlignment.Center;
//
// LastPageButton
//
LastPageButton.Anchor = AnchorStyles.Top | AnchorStyles.Right;
LastPageButton.Location = new Point(218, 3);
LastPageButton.Name = "LastPageButton";
LastPageButton.Size = new Size(33, 23);
LastPageButton.TabIndex = 3;
LastPageButton.Text = ">>";
LastPageButton.UseVisualStyleBackColor = true;
LastPageButton.Click += LastPageButton_Click;
//
// NextPageButton
//
NextPageButton.Anchor = AnchorStyles.Top | AnchorStyles.Right;
NextPageButton.Location = new Point(179, 3);
NextPageButton.Name = "NextPageButton";
NextPageButton.Size = new Size(33, 23);
NextPageButton.TabIndex = 2;
NextPageButton.Text = ">";
NextPageButton.UseVisualStyleBackColor = true;
NextPageButton.Click += NextPageButton_Click;
//
// PrevPageButton
//
PrevPageButton.Location = new Point(42, 3);
PrevPageButton.Name = "PrevPageButton";
PrevPageButton.Size = new Size(33, 23);
PrevPageButton.TabIndex = 1;
PrevPageButton.Text = "<";
PrevPageButton.UseVisualStyleBackColor = true;
PrevPageButton.Click += PrevPageButton_Click;
//
// FirstPageButton
//
FirstPageButton.Location = new Point(3, 3);
FirstPageButton.Name = "FirstPageButton";
FirstPageButton.Size = new Size(33, 23);
FirstPageButton.TabIndex = 0;
FirstPageButton.Text = "<<";
FirstPageButton.UseVisualStyleBackColor = true;
FirstPageButton.Click += FirstPageButton_Click;
// //
// ResultSplitContainer // ResultSplitContainer
// //
@@ -212,7 +114,7 @@ partial class MainViewForm
// //
// ResultSplitContainer.Panel1 // ResultSplitContainer.Panel1
// //
ResultSplitContainer.Panel1.Controls.Add(EntityTreeView); ResultSplitContainer.Panel1.Controls.Add(JsonWebView);
// //
// ResultSplitContainer.Panel2 // ResultSplitContainer.Panel2
// //
@@ -221,13 +123,19 @@ partial class MainViewForm
ResultSplitContainer.SplitterDistance = 467; ResultSplitContainer.SplitterDistance = 467;
ResultSplitContainer.TabIndex = 1; ResultSplitContainer.TabIndex = 1;
// //
// EntityTreeView // JsonWebView
// //
EntityTreeView.Dock = DockStyle.Fill; JsonWebView.AllowExternalDrop = true;
EntityTreeView.Location = new Point(0, 0); JsonWebView.CreationProperties = null;
EntityTreeView.Name = "EntityTreeView"; JsonWebView.DefaultBackgroundColor = Color.White;
EntityTreeView.Size = new Size(467, 688); JsonWebView.Dock = DockStyle.Fill;
EntityTreeView.TabIndex = 0; JsonWebView.Location = new Point(0, 0);
JsonWebView.Name = "JsonWebView";
JsonWebView.Size = new Size(467, 688);
JsonWebView.TabIndex = 0;
JsonWebView.Visible = false;
JsonWebView.ZoomFactor = 1D;
JsonWebView.CoreWebView2InitializationCompleted += JsonWebView_CoreWebView2InitializationCompleted;
// //
// DescriptionTextBox // DescriptionTextBox
// //
@@ -256,14 +164,12 @@ partial class MainViewForm
InnerSplitContainer.Panel2.ResumeLayout(false); InnerSplitContainer.Panel2.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize) InnerSplitContainer).EndInit(); ((System.ComponentModel.ISupportInitialize) InnerSplitContainer).EndInit();
InnerSplitContainer.ResumeLayout(false); InnerSplitContainer.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize) EntityGridView).EndInit();
PaginationPagel.ResumeLayout(false);
PaginationPagel.PerformLayout();
ResultSplitContainer.Panel1.ResumeLayout(false); ResultSplitContainer.Panel1.ResumeLayout(false);
ResultSplitContainer.Panel2.ResumeLayout(false); ResultSplitContainer.Panel2.ResumeLayout(false);
ResultSplitContainer.Panel2.PerformLayout(); ResultSplitContainer.Panel2.PerformLayout();
((System.ComponentModel.ISupportInitialize) ResultSplitContainer).EndInit(); ((System.ComponentModel.ISupportInitialize) ResultSplitContainer).EndInit();
ResultSplitContainer.ResumeLayout(false); ResultSplitContainer.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize) JsonWebView).EndInit();
ResumeLayout(false); ResumeLayout(false);
} }
@@ -272,15 +178,8 @@ partial class MainViewForm
private SplitContainer RootSplitContainer; private SplitContainer RootSplitContainer;
private SplitContainer InnerSplitContainer; private SplitContainer InnerSplitContainer;
private ListBox TypesListBox; private ListBox TypesListBox;
private DataGridView EntityGridView;
private TreeView EntityTreeView;
private SplitContainer ResultSplitContainer; private SplitContainer ResultSplitContainer;
private TextBox DescriptionTextBox; private TextBox DescriptionTextBox;
private Panel PaginationPagel; private Microsoft.Web.WebView2.WinForms.WebView2 JsonWebView;
private Button LastPageButton; private DatabaseViewerControl DatabaseViewer;
private Button NextPageButton;
private Button PrevPageButton;
private Button FirstPageButton;
private TextBox PageTextBox;
private TextBox PaginationLabel;
} }
+50 -250
View File
@@ -1,13 +1,12 @@
using Microsoft.EntityFrameworkCore; using Newtonsoft.Json;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters; using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization; 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.Common.I18n;
using Pf2eModel.Model.Enum; using Pf2eModel.Model.Enum;
using Pf2eViewer.Converters;
namespace Pf2eViewer; namespace Pf2eViewer;
@@ -15,12 +14,22 @@ public partial class MainViewForm : Form
{ {
public Pf2eDbContext Context = new(); public Pf2eDbContext Context = new();
public readonly string JsonViewerHtml;
public readonly EntityView Viewer;
public MainViewForm() public MainViewForm()
{ {
InitializeComponent(); InitializeComponent();
Reset();
EntityGridView.SelectionMode = DataGridViewSelectionMode.FullRowSelect; _ = new DarkModeCS(this) { ColorMode = DarkModeCS.DisplayMode.SystemDefault };
using StreamReader reader = new("Resources/json-viewer.html");
JsonViewerHtml = reader.ReadToEnd();
DatabaseViewer.EntityChange += DatabaseViewer_EntityChange;
Reset();
} }
public void Reset() public void Reset()
@@ -28,55 +37,41 @@ public partial class MainViewForm : Form
Context.Dispose(); Context.Dispose();
Context = new(); Context = new();
TypesListBox.DataSource = Context.Entities.Select(e => e.Type).Distinct().ToList(); TypesListBox.DataSource = Context
.Entities.Select(e => e.Type)
.Distinct()
.OrderBy(t => t.ToString())
.ToList();
TypesListBox.SelectedIndex = 0; TypesListBox.SelectedIndex = 0;
} }
public const int PerPage = 30; private void ToggleFreeze(bool state)
public int Count;
public int Page
{ {
get => Convert.ToInt32(PageTextBox.Text); // EntityGridView.Visible = state;
set // JsonWebView.Visible = state;
{
PageTextBox.Text = value.ToString();
UpdateTreeView();
}
} }
public int MaxPage => Convert.ToInt32(Math.Ceiling(1.0 * Count / PerPage)); private void DatabaseViewer_EntityChange(EntityView newEntity)
private void UpdateTreeView()
{ {
EntityTreeView.Nodes.Clear(); JsonSerializerSettings settings = new()
EntityType type = (EntityType)TypesListBox.SelectedItem!;
Count = Context.Entities.Count(e => e.Type == type);
if (Page < 1 || Page > MaxPage)
{ {
Page = Math.Min(MaxPage, Math.Max(1, Page)); Converters =
return; [
} new StringEnumConverter { NamingStrategy = new CamelCaseNamingStrategy() },
new EntityConverter(),
new LocaleStringConverter(),
],
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
};
EntityGridView.DataSource = Context string result = JsonConvert.SerializeObject(newEntity.Entity, settings);
.Entities.Where(e => e.Type == (EntityType)TypesListBox.SelectedItem) string subResult = JsonConvert.SerializeObject(newEntity.SubEntity, settings);
.OrderBy(e => e.Id)
.Select(e => new EntityGridRow(e))
.Skip((Page - 1) * PerPage)
.Take(PerPage)
.ToList();
EntityGridView.Rows[0].Selected = true; string json = result[..^1] + $",\"SubEntity\":{subResult}}}";
PaginationLabel.Text = $"Всего: {Count} объектов, {MaxPage} страниц"; RenderJson(json);
FirstPageButton.Enabled = Page > 1; DescriptionTextBox.Text = newEntity.Entity!.Description;
PrevPageButton.Enabled = Page > 1;
NextPageButton.Enabled = Page < MaxPage;
LastPageButton.Enabled = Page < MaxPage;
} }
private void TypesListBox_SelectedIndexChanged(object sender, EventArgs _) private void TypesListBox_SelectedIndexChanged(object sender, EventArgs _)
@@ -86,223 +81,28 @@ public partial class MainViewForm : Form
return; return;
} }
Page = 1; DatabaseViewer.Type = (EntityType)TypesListBox.SelectedItem;
// Viewer.Page = 1;
} }
private void FirstPageButton_Click(object sender, EventArgs e) public void RenderJson(string json)
{ {
Page = 1; if (JsonWebView.CoreWebView2 == null)
{
JsonWebView.EnsureCoreWebView2Async();
return;
} }
private void PrevPageButton_Click(object sender, EventArgs e) string html = JsonViewerHtml.Replace("{JSON}", json);
{ JsonWebView.CoreWebView2.NavigateToString(html);
Page--;
} }
private void NextPageButton_Click(object sender, EventArgs e) private void JsonWebView_CoreWebView2InitializationCompleted(
{
Page++;
}
private void LastPageButton_Click(object sender, EventArgs e)
{
Page = MaxPage;
}
private void PageTextBox_KeyPress(object sender, KeyPressEventArgs e)
{
if (!char.IsControl(e.KeyChar) && !char.IsDigit(e.KeyChar))
{
e.Handled = true;
}
if (e.KeyChar == (char)Keys.Return)
{
UpdateTreeView();
}
}
private void EntityGridView_RowStateChanged(
object sender, object sender,
DataGridViewRowStateChangedEventArgs e Microsoft.Web.WebView2.Core.CoreWebView2InitializationCompletedEventArgs e
) )
{ {
int rowsCount = EntityGridView.SelectedRows.Count; JsonWebView.Visible = true;
if (rowsCount is 0 or > 1) DatabaseViewer_EntityChange(DatabaseViewer.CurrentEntity);
{
return;
}
DataGridViewRow row = EntityGridView.SelectedRows[0];
EntityGridRow data = (EntityGridRow)row.DataBoundItem;
ViewEntity(data.Id);
}
public void ViewEntity(int id)
{
EntityTreeView.Nodes.Clear();
Entity? entity = Context
.Entities.Where(e => e.Id == id && e.Type == (EntityType)TypesListBox.SelectedItem!)
.Include(e => e.Sources)
.ThenInclude(s => s.Source)
.Include(e => e.Keywords)
.Include(e => e.Externals)
.ThenInclude(x => x.Target)
.Include(e => e.Traits)
.ThenInclude(t => t.Entity)
.FirstOrDefault();
if (entity == null)
{
DescriptionTextBox.Text = "Not found";
return;
}
string json = SerializeEntity(entity);
JsonToTreeview(EntityTreeView, json, "Entity");
DescriptionTextBox.Text = entity.Description.Replace("\\r", "").Replace("\\n", "\\r\\n");
}
public string SerializeEntity(Entity entity)
{
JsonSerializerSettings settings = new()
{
Converters =
[
new EntityConverter(),
new StringEnumConverter { NamingStrategy = new CamelCaseNamingStrategy() },
],
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
};
string result = JsonConvert.SerializeObject(entity, settings);
Type type = EntityOfTypeAttribute.Resolve(entity);
string subResult = JsonConvert.SerializeObject(ResolveSubEntity(entity), settings);
return result[..^1] + $",\"SubEntity\":{subResult}}}";
}
public IEntity? ResolveSubEntity(Entity entity)
{
switch (entity.Type)
{
case EntityType.Ancestry:
return Context
.Ancestries.Include(a => a.AncestryBoosts)
.FirstOrDefault(e => e.Id == entity.Id);
case EntityType.Archetype:
return Context
.Archetypes.Include(a => a.DedicationFeat)
.ThenInclude(f => f.Entity)
.Include(a => a.Feats)
.ThenInclude(f => f.Entity)
.FirstOrDefault(a => a.Id == entity.Id);
case EntityType.Background:
return Context
.Backgrounds.Include(b => b.BackgroundBoosts)
.Include(b => b.BackgroundSkills)
.ThenInclude(t => t.Training)
.Include(b => b.BackgroundItems)
.FirstOrDefault(e => e.Id == entity.Id);
case EntityType.Class:
return Context
.Classes.Include(c => c.ClassKeyAbilities)
.Include(c => c.ClassTrainings)
.ThenInclude(t => t.Training)
.Include(c => c.ClassProgressions)
.FirstOrDefault(c => c.Id == entity.Id);
case EntityType.Feat:
return Context
.Feats.Include(f => f.Archetypes)
.ThenInclude(a => a.Entity)
.FirstOrDefault(f => f.Id == entity.Id);
default:
Type type = EntityOfTypeAttribute.Resolve(entity);
return (IEntity?)Context.Find(type, entity.Id);
}
}
public static void JsonToTreeview(TreeView treeView, string jsonString, string rootName)
{
object json = jsonString.StartsWith('[')
? JArray.Parse(jsonString)
: JObject.Parse(jsonString);
treeView.Nodes.Clear();
foreach (TreeNode node in Element2TreeNode(json, rootName).Nodes)
{
treeView.Nodes.Add(node);
}
treeView.ExpandAll();
}
private static TreeNode Element2TreeNode(object element, string text = "")
{
List<TreeNode> subNodes = [];
switch (element.GetType().Name)
{
case "JObject":
JObject jObject = (JObject)element;
foreach (KeyValuePair<string, JToken> token in jObject)
{
subNodes.Add(Element2TreeNode(token.Value, token.Key));
}
break;
case "JArray":
JArray jArray = (JArray)element;
int i = 0;
subNodes.AddRange(jArray.Select(token => Element2TreeNode(token, $"[{i++}]")));
if (i == 0)
{
text += ": []";
}
break;
case "JValue":
JValue jValue = (JValue)element;
text += $": ({jValue.Type}) {jValue}";
break;
default:
throw new Exception(
"clsJSON2Treeview can't interpret object:" + element.GetType().Name
);
}
TreeNode treeNode = new(text);
if (subNodes.Count > 0)
{
treeNode.Nodes.AddRange(
subNodes.OrderBy(node => node.Nodes.Count > 0 ? 1 : 0).ToArray()
);
}
return treeNode;
}
private class EntityGridRow(Entity e)
{
public int Id { get; set; } = e.Id;
public string Name { get; set; } = e.Name[LocaleType.English];
public EntityRarity Rarity { get; set; } = e.Rarity;
private readonly Entity _e = e;
public Entity GetEntity() => _e;
} }
} }
+6
View File
@@ -7,6 +7,7 @@
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.3179.45" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@@ -15,4 +16,9 @@
<ItemGroup> <ItemGroup>
<Folder Include="Properties\" /> <Folder Include="Properties\" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Update="Resources\json-viewer.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project> </Project>
+24
View File
@@ -0,0 +1,24 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>JSON Viewer</title>
<script src="https://pfau-software.de/json-viewer/dist/iife/index.js"></script>
<style>
body {
margin: 0;
background: black;
}
</style>
</head>
<body>
<andypf-json-viewer indent="4"
expanded="true"
theme="classic-dark"
expand-icon-type="square"
show-data-types="false"
show-toolbar="false"
show-copy="false"
show-size="true">
{JSON}
</andypf-json-viewer>
</body>
</html>