Большой рефактор

This commit is contained in:
2025-04-26 16:56:29 +04:00
parent 9d0683a23f
commit 76167f1b24
83 changed files with 943 additions and 864 deletions
+6
View File
@@ -4,6 +4,11 @@ root = true
# Файлы C#
[*.cs]
#### Resharper ####
resharper_member_can_be_private_global_highlighting = none
#### Основные параметры EditorConfig ####
# Отступы и интервалы
@@ -265,3 +270,4 @@ dotnet_code_quality_unused_parameters = all:suggestion
dotnet_style_operator_placement_when_wrapping = beginning_of_line
tab_width = 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
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Элементы решения", "Элементы решения", "{754FC069-D67B-A9D7-50A1-8D1CA196D8F1}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
README.md = README.md
EndProjectSection
EndProject
+5 -9
View File
@@ -5,21 +5,17 @@ namespace Pf2eDataScrapper.Importers;
public class DatabaseHelper
{
public Pf2eDbContext Context = new();
public readonly Pf2eDbContext Context = new();
public SlugHelper Slugger = new();
public readonly SlugHelper Slugger = new();
private int _id = 0;
private int _id;
public int NextEntityId
{
get => ++_id;
set => _id = value;
}
public int NextEntityId => ++_id;
private DatabaseHelper()
{
NextEntityId = Context.Entities.Any() ? Context.Entities.Max(e => e.Id) : 0;
_id = Context.Entities.Any() ? Context.Entities.Max(e => e.Id) : 0;
}
public string GenerateSlug(string name)
@@ -40,7 +40,7 @@ internal class AonprdLoader(AonprdImporter importer)
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"));
JArray array = JArray.Parse(fileReader.ReadToEnd());
@@ -1,13 +1,13 @@
using Konsole;
using Newtonsoft.Json.Linq;
using Pf2eDataScrapper.Importers.Implementations.Aonprd.Compendium;
using Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors;
using static Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.AbstractProcessor;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder;
internal class AonprdDataBuilder(AonprdLoader loader)
{
public Pf2eDatabase Database = Pf2eDatabase.Instance;
public readonly Pf2eDatabase Database = Pf2eDatabase.Instance;
/// <summary>
/// Загрузчик данных.
@@ -19,7 +19,7 @@ internal class AonprdDataBuilder(AonprdLoader loader)
/// </summary>
public AonprdCompendium Compendium => Loader.Compendium;
public AonprdProcessorCollection Processors = new();
public readonly AonprdProcessorCollection Processors = new();
public void TruncateTables()
{
@@ -30,64 +30,79 @@ internal class AonprdDataBuilder(AonprdLoader loader)
Console.WriteLine("Truncation completed!");
}
public void Build()
protected ConcurrentWriter Window = null!;
protected Window Output = null!;
protected ProgressBar ProcessProgressBar = null!;
protected ProgressBar SubProcessProgressBar = null!;
protected void ConfigureWindow()
{
Console.WriteLine("Building data...");
Window = new Window(0, 0, Console.WindowWidth, Console.WindowHeight).Concurrent();
ConcurrentWriter window = new Window(
0,
0,
Console.WindowWidth,
Console.WindowHeight
).Concurrent();
IConsole bars = window.OpenBox(
IConsole bars = Window.OpenBox(
"Progress",
Console.WindowWidth,
4,
new BoxStyle { Body = Colors.WhiteOnBlack }
new() { Body = Colors.WhiteOnBlack }
);
Window output = new(0, 4, Console.WindowWidth, Console.WindowHeight - 4, window);
Output = new(0, 4, Console.WindowWidth, Console.WindowHeight - 4, Window);
ProgressBar bar1 = new(bars, Processors.Items.Count, 15);
ProgressBar bar2 = new(bars, 1, 15);
int i = 0;
ProcessProgressBar = new(bars, Processors.Items.Count, 15);
SubProcessProgressBar = new(bars, 1, 15);
}
protected void RunProcessor(AonprdProcessorCollection.ProcessorItem item, JObject json)
{
AonprdProcessorInput input = new(json);
AbstractProcessor processor = (AbstractProcessor)item.Constructor.Invoke([input]);
try
{
processor.Process();
processor.Save();
}
catch (LegacyItemException ex)
{
Output.WriteLine(ex.Message);
}
catch (SkipItemException ex)
{
Output.WriteLine(ex.Message);
}
}
public void Build()
{
ConfigureWindow();
foreach (AonprdProcessorCollection.ProcessorItem item in Processors.Iterate())
{
bar1.Refresh(i++, $"{item.Attribute.Name}");
ProcessProgressBar.Next(item.Attribute.Name);
bar2.Max = Compendium.Count([item.Attribute.Type]);
int j = 0;
if (item.Attribute.Type is null)
{
RunProcessor(item, []);
continue;
}
SubProcessProgressBar.Max = Compendium.Count([item.Attribute.Type]);
foreach (AonprdLoader.LoaderEntry entry in Loader.Entries(item.Attribute.Type))
{
bar2.Refresh(j++, $"{entry.Id}");
AonprdProcessorInput input = new(entry.Json);
AbstractProcessor processor = (AbstractProcessor)item.Constructor.Invoke([input]);
try
{
processor.Process();
processor.Save();
}
catch (LegacyItemException ex)
{
output.WriteLine(ex.Message);
}
catch (SkipItemException ex)
{
output.WriteLine(ex.Message);
}
SubProcessProgressBar.Next($"{entry.Id}");
RunProcessor(item, entry.Json);
}
bar2.Refresh(j, "Processor completed");
SubProcessProgressBar.Next("Ready");
}
bar1.Refresh(i, "Done!");
ProcessProgressBar.Next("Done!");
window.Clear();
Window.Clear();
}
}
@@ -1,11 +1,11 @@
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder;
[AttributeUsage(AttributeTargets.Class)]
public class AonprdProcessorAttribute(string name, string type, string[] dependencies) : Attribute
public class AonprdProcessorAttribute(string name, string? type, string[] dependencies) : Attribute
{
public readonly string Name = name;
public readonly string Type = type;
public readonly string? Type = type;
public readonly IEnumerable<string> Dependencies = dependencies;
@@ -1,6 +1,7 @@
using System.Reflection;
using Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder;
internal class AonprdProcessorCollection
{
@@ -41,13 +42,13 @@ internal class AonprdProcessorCollection
);
if (collision is not null)
{
throw new Exception(
throw new(
$"Multiply attributes 'AonprdProcessor' have same name '{attribute.Name}'."
+ $" They are added to classes: '{type.FullName}' and '{collision.Constructor.DeclaringType!.FullName}'"
);
}
Items.Add(new ProcessorItem(attribute, constructor));
Items.Add(new(attribute, constructor));
}
}
@@ -65,7 +66,7 @@ internal class AonprdProcessorCollection
}
IEnumerable<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))
@@ -1,4 +1,5 @@
using Newtonsoft.Json.Linq;
using Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Helpers;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder;
@@ -84,10 +85,10 @@ internal class AonprdProcessorInput(JObject input) : ProcessorInput(input)
public class ActionStruct
{
public int Actions = 0;
public int MaxActions = 0;
public bool IsPassive = false;
public bool IsReaction = false;
public bool IsFreeAction = false;
public int Actions;
public int MaxActions;
public bool IsPassive;
public bool IsReaction;
public bool IsFreeAction;
}
}
@@ -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;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Helpers;
public static class AonprdEnumResolver
{
@@ -1,16 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Text.RegularExpressions;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Helpers;
internal partial class StringHelper
{
private StringHelper() { }
public string ResolveSource(string input)
{
Match match = SourceSuffixRegex().Match(input);
return match.Captures.Count > 0 ? match.Captures[0].Value : "";
}
public static StringHelper Instance { get; } = new();
[GeneratedRegex(@"^\[.*?\]\(.*?\)\s+(.*)$")]
@@ -0,0 +1,224 @@
using System.Security.Cryptography;
using System.Text;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Pf2eModel;
using Pf2eModel.Model;
using Pf2eModel.Model.Base;
using Pf2eModel.Model.Base.Source;
using Pf2eModel.Model.Base.Targets;
using Pf2eModel.Model.Common.I18n;
using Pf2eModel.Model.Enum;
using Pf2eModel.Model.Structure;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors;
internal abstract class AbstractEntityProcessor(AonprdProcessorInput input)
: AbstractProcessor(input)
{
public Entity Entity { get; } = new();
public abstract EntityType Type { get; }
public abstract IEntity Object { get; }
public abstract void Add();
public override void Process()
{
if (Input.GetOptional<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)
.ThenInclude(entity => entity.Name)
.Where(t => traitEntities.Contains(t.Entity))
.ToList();
foreach (string name in names)
{
if (!traits.Any(t => t.Entity.Name.Contains(name)))
{
throw new Exception($"Trait '{name}' is not found!!!");
}
}
foreach (Trait trait in traits)
{
Entity.Traits.Add(trait);
}
}
protected void ProcessLegacy(string id, string name)
{
if (RemasterWithoutLegacy.TryGetValue(id, out Entity? remastered))
{
// if (remastered.ToString() != name)
remastered.Name[LocaleType.LegacyEnglish] = name;
RemasterWithoutLegacy.Remove(id);
}
else
{
LegacyAliases.Add(id, name);
}
}
public string GenerateExtra(params string[] args)
{
Dictionary<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,250 +1,21 @@
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Helpers;
using Pf2eModel;
using Pf2eModel.Model;
using Pf2eModel.Model.Base;
using Pf2eModel.Model.Base.Source;
using Pf2eModel.Model.Base.Targets;
using Pf2eModel.Model.Common.I18n;
using Pf2eModel.Model.Enum;
using Pf2eModel.Model.Structure;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors;
internal abstract partial class AbstractProcessor(AonprdProcessorInput input)
internal abstract class AbstractProcessor(AonprdProcessorInput input)
{
protected DatabaseHelper Helper = DatabaseHelper.Instance;
protected readonly DatabaseHelper Helper = DatabaseHelper.Instance;
protected StringHelper StringHelper = StringHelper.Instance;
protected readonly StringHelper StringHelper = StringHelper.Instance;
protected readonly AonprdBuilderGlobals Globals = AonprdBuilderGlobals.Instance;
public readonly Pf2eDbContext Context = DatabaseHelper.DbContext;
public readonly AonprdProcessorInput Input = input;
public Entity Entity { get; set; } = new();
public abstract void Process();
public abstract EntityType Type { get; }
public abstract IEntity Object { get; }
protected abstract void Add();
public virtual void Process()
{
if (Input.GetOptional<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}.") { }
public abstract void Save();
}
@@ -1,10 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Pf2eModel;
using Pf2eModel.Model;
using Pf2eModel;
using Pf2eModel.Model.Base;
using Pf2eModel.Model.Common.I18n;
using Pf2eModel.Model.Enum;
@@ -13,7 +7,7 @@ using Pf2eModel.Model.Structure;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors;
internal abstract class AbstractSubclassProcessor(AonprdProcessorInput input)
: AbstractProcessor(input)
: AbstractEntityProcessor(input)
{
public override EntityType Type => EntityType.Subclass;
@@ -23,7 +17,7 @@ internal abstract class AbstractSubclassProcessor(AonprdProcessorInput input)
public abstract string GroupName { get; }
protected override void Add() => Context.Subclasses.Add(Object);
public override void Add() => Context.Subclasses.Add(Object);
public override void Process()
{
@@ -37,51 +31,40 @@ internal abstract class AbstractSubclassProcessor(AonprdProcessorInput input)
public SubclassGroup GetGroup()
{
SubclassGroup? group = Context.SubclassGroups.FirstOrDefault(g =>
// ReSharper disable once EntityFramework.UnsupportedServerSideFunctionCall
Pf2eDbContext.LocaleSearch(g.Entity.Name, GroupName)
);
if (group is null)
if (group is not null)
{
Class? cl =
(ClassName is not null)
? Context.Classes.FirstOrDefault(c =>
Pf2eDbContext.LocaleSearch(c.Entity.Name, GroupName)
)
: null;
Entity groupEntity = new()
{
Id = Helper.NextEntityId,
Type = EntityType.SubclassGroup,
Img = "",
Description = "",
Rarity = EntityRarity.Common,
IsTranslated = false,
IsChecked = false,
};
groupEntity.Name[LocaleType.English] = GroupName;
groupEntity.Slug = Helper.GenerateSlug(GroupName);
Context.Entities.Add(groupEntity);
/*
Entity.Id = Helper.NextEntityId;
Entity.Type = Type;
Entity.Name[LocaleType.English] = Input.GetRequired<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);
return group;
}
Class? cl = ClassName is not null
? Context.Classes.FirstOrDefault(c =>
// ReSharper disable once EntityFramework.UnsupportedServerSideFunctionCall
Pf2eDbContext.LocaleSearch(c.Entity.Name, GroupName)
)
: null;
Entity groupEntity = new()
{
Id = Helper.NextEntityId,
Type = EntityType.SubclassGroup,
Img = "",
Description = "",
Rarity = EntityRarity.Common,
IsTranslated = false,
IsChecked = false,
Name = { [LocaleType.English] = GroupName },
Slug = Helper.GenerateSlug(GroupName),
};
Context.Entities.Add(groupEntity);
group = new() { Entity = groupEntity, Class = cl };
Context.SubclassGroups.Add(group);
return group;
}
}
@@ -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;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Common;
[AonprdProcessor("source", new string[] { })]
[AonprdProcessor("source", [])]
internal class SourceProcessor(AonprdProcessorInput input) : AbstractProcessor(input)
{
public override EntityType Type => EntityType.Unknown;
public override IEntity? Object => null;
protected override void Add() { }
protected Source Source = null!;
public override void Process()
{
// base.Process();
Source source = new()
Source = new()
{
Name = Input.GetRequired<string>("name"),
Category = Input.EnumRequired<SourceCategory>("primary_source_category"),
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);
}
}
@@ -7,7 +7,7 @@ internal class ApparitionProcessor(AonprdProcessorInput input) : AbstractSubclas
{
public override Subclass Object { get; } = new();
public override string? ClassName => "Animist";
public override string ClassName => "Animist";
public override string GroupName => "Apparition";
@@ -3,13 +3,13 @@
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types;
[AonprdProcessor("action")]
internal class ActionProcessor(AonprdProcessorInput input) : AbstractProcessor(input)
internal class ActionProcessor(AonprdProcessorInput input) : AbstractEntityProcessor(input)
{
public override EntityType Type => EntityType.Action;
public override Pf2eModel.Model.Structure.Action Object { get; } = new();
protected override void Add() => Context.Actions.Add(Object);
public override void Add() => Context.Actions.Add(Object);
public override void Process()
{
@@ -24,7 +24,7 @@ internal class ActionProcessor(AonprdProcessorInput input) : AbstractProcessor(i
Object.IsFreeAction = action.IsFreeAction;
Object.IsPassive = action.IsPassive;
Object.IsReaction = action.IsReaction;
;
Object.Extra = GenerateExtra("school");
}
@@ -1,19 +1,17 @@
using LinqToDB;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Linq;
using Pf2eModel.Model.Enum;
using Pf2eModel.Model.Structure;
using Pf2eModel.Model.Structure.AncestryRelation;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types;
[AonprdProcessor("ancestry", ["trait", "language"])]
internal class AncestryProcessor(AonprdProcessorInput input) : AbstractProcessor(input)
internal class AncestryProcessor(AonprdProcessorInput input) : AbstractEntityProcessor(input)
{
public override EntityType Type => EntityType.Ancestry;
public override Ancestry Object { get; } = new();
protected override void Add() => Context.Ancestries.Add(Object);
public override void Add() => Context.Ancestries.Add(Object);
public override void Process()
{
@@ -36,7 +34,7 @@ internal class AncestryProcessor(AonprdProcessorInput input) : AbstractProcessor
Object.Speed = Convert.ToInt32(Input.GetOptional<int>("speed.land"));
Object.Vision = Input.EnumOptional<AncestryVision>("vision", AncestryVision.Normal);
Object.Vision = Input.EnumOptional("vision", AncestryVision.Normal);
AddBoosts("attribute", true);
AddBoosts("attribute_flaw", false);
@@ -59,14 +57,14 @@ internal class AncestryProcessor(AonprdProcessorInput input) : AbstractProcessor
foreach (string boost in boosts)
{
AncestryBoost ancestryBoost = new()
{
Ancestry = Object,
Boost = isBoost,
Value = Input.Enum<AttributeValues>(boost),
};
Context.AncestryBoosts.Add(ancestryBoost);
Context.AncestryBoosts.Add(
new()
{
Ancestry = Object,
Boost = isBoost,
Value = Input.Enum<AttributeValues>(boost),
}
);
}
}
}
@@ -4,13 +4,13 @@ using Pf2eModel.Model.Structure;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types;
[AonprdProcessor("archetype")]
internal class ArchetypeProcessor(AonprdProcessorInput input) : AbstractProcessor(input)
internal class ArchetypeProcessor(AonprdProcessorInput input) : AbstractEntityProcessor(input)
{
public override EntityType Type => EntityType.Archetype;
public override Archetype Object { get; } = new();
protected override void Add() => Context.Archetypes.Add(Object);
public override void Add() => Context.Archetypes.Add(Object);
public override void Process()
{
@@ -6,13 +6,13 @@ using Pf2eModel.Model.Structure.BackgroundRelation;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types;
[AonprdProcessor("background")]
internal class BackgroundProcessor(AonprdProcessorInput input) : AbstractProcessor(input)
internal class BackgroundProcessor(AonprdProcessorInput input) : AbstractEntityProcessor(input)
{
public override EntityType Type => EntityType.Background;
public override Background Object { get; } = new();
protected override void Add() => Context.Backgrounds.Add(Object);
public override void Add() => Context.Backgrounds.Add(Object);
public override void Process()
{
@@ -1,6 +1,4 @@
using System.Text.RegularExpressions;
using LinqToDB;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json.Linq;
using Pf2eModel.Model.Common;
using Pf2eModel.Model.Enum;
@@ -10,13 +8,13 @@ using Pf2eModel.Model.Structure.ClassRelation;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types;
[AonprdProcessor("class", ["trait", "source", "skill"])]
internal class ClassProcessor(AonprdProcessorInput input) : AbstractProcessor(input)
internal class ClassProcessor(AonprdProcessorInput input) : AbstractEntityProcessor(input)
{
public override EntityType Type => EntityType.Class;
public override Class Object { get; } = new();
protected override void Add() => Context.Classes.Add(Object);
public override void Add() => Context.Classes.Add(Object);
public override void Process()
{
@@ -156,7 +154,6 @@ internal class ClassProcessor(AonprdProcessorInput input) : AbstractProcessor(in
.Where(s => s != "")
.Select(s => string.Concat(s[0].ToString().ToUpper(), s.AsSpan(1)))
.ToArray();
;
}
protected ICollection<ClassTraining> GetSaveTrainings()
@@ -6,13 +6,13 @@ using Pf2eModel.Model.Structure;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types;
// [AonprdProcessor("feat", ["trait", "source", "archetype"])]
internal class FeatProcessor(AonprdProcessorInput input) : AbstractProcessor(input)
internal class FeatProcessor(AonprdProcessorInput input) : AbstractEntityProcessor(input)
{
public override EntityType Type => EntityType.Feat;
public override Feat Object { get; } = new();
protected override void Add() => Context.Feats.Add(Object);
public override void Add() => Context.Feats.Add(Object);
public override void Process()
{
@@ -47,6 +47,7 @@ internal class FeatProcessor(AonprdProcessorInput input) : AbstractProcessor(inp
Input.GetOptional<JArray>("archetype")?.Select(t => t.ToString()).ToArray() ?? [];
Archetype[] archetypes = Context
// ReSharper disable once EntityFramework.UnsupportedServerSideFunctionCall
.Archetypes.Where(a => Pf2eDbContext.LocaleSearchArray(a.Entity.Name, archetypeStrings))
// .Where(a => archetypeStrings.Contains(a.Entity.Name.ToString()))
.ToArray();
@@ -56,7 +57,7 @@ internal class FeatProcessor(AonprdProcessorInput input) : AbstractProcessor(inp
Object.Category = ActionCategory.Archetype;
}
foreach (Archetype? archetype in archetypes)
foreach (Archetype archetype in archetypes)
{
Object.Archetypes.Add(archetype);
@@ -4,16 +4,11 @@ using Pf2eModel.Model.Structure;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types;
[AonprdProcessor("language")]
internal class LanguageProcessor(AonprdProcessorInput input) : AbstractProcessor(input)
internal class LanguageProcessor(AonprdProcessorInput input) : AbstractEntityProcessor(input)
{
public override EntityType Type => EntityType.Language;
public override Language Object { get; } = new();
protected override void Add() => Context.Languages.Add(Object);
public override void Process()
{
base.Process();
}
public override void Add() => Context.Languages.Add(Object);
}
@@ -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;
[AonprdProcessor("trait", new string[] { })]
internal class TraitProcessor(AonprdProcessorInput input) : AbstractProcessor(input)
[AonprdProcessor("trait", ["external"])]
internal class TraitProcessor(AonprdProcessorInput input) : AbstractEntityProcessor(input)
{
public override EntityType Type => EntityType.Trait;
public override Trait Object { get; } = new();
protected override void Add() => Context.Traits.Add(Object);
public override void Add() => Context.Traits.Add(Object);
public override void Process()
{
@@ -46,7 +46,7 @@ internal class CompendiumFolder
if (json.TryGetValue("_id", out JToken? id))
{
// Вложенная папка через _folders.json
Id = id!.ToString();
Id = id.ToString();
Name = json["name"]!.ToString();
Path = null;
IsContained = true;
@@ -22,12 +22,12 @@ internal class CompendiumLoader(Pf2EFoundryImporter importer)
/// <summary>
/// Счётчик папок
/// </summary>
private int _folderCount = 0;
private int _folderCount;
/// <summary>
/// Общее количество элементов в компендиуме
/// </summary>
public int ItemCount { get; protected set; } = 0;
public int ItemCount { get; protected set; }
/// <summary>
/// Обработка папок компендиума в поле `packFolders`.
@@ -48,7 +48,7 @@ internal class CompendiumLoader(Pf2EFoundryImporter importer)
if (obj.TryGetValue("packs", out JToken? packs))
{
foreach (JToken jToken in packs!)
foreach (JToken jToken in packs)
{
string packId = jToken.ToString();
CompendiumFolder? child = Collection.GetById(packId);
@@ -62,7 +62,7 @@ internal class CompendiumLoader(Pf2EFoundryImporter importer)
if (obj.TryGetValue("folders", out JToken? innerFolders))
{
ProcessPackFolders((JArray)innerFolders!, folder);
ProcessPackFolders((JArray)innerFolders, folder);
}
}
@@ -90,7 +90,7 @@ internal class CompendiumLoader(Pf2EFoundryImporter importer)
}
// Поиск _folders.json
foreach (CompendiumFolder? parent in Collection.ToList())
foreach (CompendiumFolder parent in Collection.ToList())
{
if (parent.Path is null)
{
@@ -1,5 +1,6 @@
using System.Security.Cryptography;
using System.Text;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json.Linq;
using Pf2eModel;
using Pf2eModel.Model;
@@ -39,10 +40,7 @@ internal abstract class AbstractProcessor(ProcessorInput input)
Entity.IsChecked = false;
Entity.Slug = Helper.GenerateSlug(Entity.Name[LocaleType.English]);
Entity.Rarity = Input.EnumOptional<EntityRarity>(
"system.traits.rarity",
EntityRarity.Common
);
Entity.Rarity = Input.EnumOptional("system.traits.rarity", EntityRarity.Common);
Object.Id = Entity.Id;
@@ -140,16 +138,19 @@ internal abstract class AbstractProcessor(ProcessorInput input)
protected IEnumerable<Trait> GetTraits(IEnumerable<string> names)
{
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();
/*
foreach (string name in names)
{
if (result.Any(t => t.Entity.Name.Contains(name)))
{
continue;
}
/*
Trait trait = new()
{
Name = name,
@@ -159,8 +160,8 @@ internal abstract class AbstractProcessor(ProcessorInput input)
};
Context.Traits.Add(trait);
result.Add(trait);
*/
}
*/
return result;
}
@@ -24,7 +24,7 @@ internal class ConditionProcessor(ProcessorInput input) : AbstractProcessor(inpu
};
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);
}
@@ -2,10 +2,6 @@
namespace Pf2eDataScrapper.Importers.Repositories;
/// <summary>
/// Загрузчик данных из git-репозиториев
/// </summary>
/// <param name="repositoryDirPath"></param>
internal class GitRepository(AbstractImporter importer, string url)
: AbstractImporterRepository(importer)
{
+12 -25
View File
@@ -11,7 +11,7 @@ internal class SchemaNode
/// <summary>
/// Отображаемое имя элемента.
/// </summary>
public string Name = "";
public readonly string Name = "";
/// <summary>
/// Путь к элементу.
@@ -21,12 +21,12 @@ internal class SchemaNode
/// <summary>
/// Родительский элемент.
/// </summary>
public SchemaNode? Parent = null;
public readonly SchemaNode? Parent;
/// <summary>
/// Указание, является ли элемент корневым.
/// </summary>
public bool Root = false;
public readonly bool Root;
/// <summary>
/// Корневой элемент схемы данных.
@@ -36,12 +36,12 @@ internal class SchemaNode
/// <summary>
/// Типы данных, использующиеся в элементе.
/// </summary>
public List<AbstractSchemaType> Types = [];
public readonly List<AbstractSchemaType> Types = [];
/// <summary>
/// Указание, может ли этот элемент отсутсвовать в структуре данных.
/// </summary>
public bool IsNullable = false;
public bool IsNullable;
/// <summary>
/// Объект для определения типа поля в схеме данных.
@@ -51,7 +51,7 @@ internal class SchemaNode
/// <summary>
/// Количество значений, добавленных в тип.
/// </summary>
public int Values { get; protected set; } = 0;
public int Values { get; protected set; }
/// <summary>
/// Указание, является ли тип элемента простым.
@@ -93,13 +93,10 @@ internal class SchemaNode
Values++;
foreach (AbstractSchemaType type in Types)
foreach (AbstractSchemaType type in Types.Where(type => type.IsCompatibleValue(value)))
{
if (type.IsCompatibleValue(value))
{
type.AddValue(value);
return;
}
type.AddValue(value);
return;
}
AbstractSchemaType newType = Resolver.Resolve(this, value);
@@ -161,7 +158,7 @@ internal class SchemaNode
foreach (AbstractSchemaType type in Types)
{
types.Add(Indent(type?.ToString() ?? ""));
types.Add(Indent(type.ToString()));
}
return FormatYaml(result, new() { ["types"] = "- " + string.Join("\r\n- ", types) });
@@ -223,19 +220,9 @@ internal class SchemaNode
{
Dictionary<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;
}
else
{
result.Add(kvp.Key, kvp.Value);
}
}
result[kvp.Key] = kvp.Value;
}
return result;
@@ -13,7 +13,7 @@ using Pf2eModel.Model.Enum;
namespace Pf2eModel.Migrations
{
[DbContext(typeof(Pf2eDbContext))]
[Migration("20250425164659_1")]
[Migration("20250426125550_1")]
partial class _1
{
/// <inheritdoc />
@@ -182,11 +182,18 @@ namespace Pf2eModel.Migrations
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Name")
.IsRequired()
.HasColumnType("jsonb")
.HasColumnName("name")
.HasColumnOrder(2);
b.Property<string>("Slug")
.IsRequired()
.HasColumnType("text")
.HasMaxLength(255)
.HasColumnType("character varying(255)")
.HasColumnName("slug")
.HasColumnOrder(2);
.HasColumnOrder(3);
b.HasKey("Id")
.HasName("pk_keywords");
@@ -1457,7 +1464,8 @@ namespace Pf2eModel.Migrations
b.Property<string>("Extra")
.IsRequired()
.HasColumnType("text")
.HasColumnName("extra");
.HasColumnName("extra")
.HasColumnOrder(4);
b.Property<int>("GroupId")
.HasColumnType("integer")
@@ -91,7 +91,8 @@ namespace Pf2eModel.Migrations
{
id = table.Column<int>(type: "integer", nullable: false)
.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 =>
{
@@ -179,11 +179,18 @@ namespace Pf2eModel.Migrations
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Name")
.IsRequired()
.HasColumnType("jsonb")
.HasColumnName("name")
.HasColumnOrder(2);
b.Property<string>("Slug")
.IsRequired()
.HasColumnType("text")
.HasMaxLength(255)
.HasColumnType("character varying(255)")
.HasColumnName("slug")
.HasColumnOrder(2);
.HasColumnOrder(3);
b.HasKey("Id")
.HasName("pk_keywords");
@@ -1454,7 +1461,8 @@ namespace Pf2eModel.Migrations
b.Property<string>("Extra")
.IsRequired()
.HasColumnType("text")
.HasColumnName("extra");
.HasColumnName("extra")
.HasColumnOrder(4);
b.Property<int>("GroupId")
.HasColumnType("integer")
+3 -2
View File
@@ -12,13 +12,13 @@ namespace Pf2eModel.Model.Base;
public class Entity
{
[System.ComponentModel.DataAnnotations.Key]
[Key]
[Column(Order = 1)]
public int Id { get; set; }
[Column(Order = 2)]
[Comment("Отображаемое имя")]
public LocaleString Name { get; set; } = new();
public LocaleString Name { get; set; } = [];
[Column(Order = 3)]
[Comment("Тип объекта")]
@@ -36,6 +36,7 @@ public class Entity
[Column(Order = 6)]
[Comment("Текстовое описание объекта")]
// ReSharper disable once EntityFramework.ModelValidation.UnlimitedStringLength
public string Description { get; set; } = null!;
[Column(Order = 7)]
+8 -3
View File
@@ -1,9 +1,10 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Pf2eModel.Model.Common.I18n;
namespace Pf2eModel.Model.Base.Keywords;
public partial class Keyword
public class Keyword
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
@@ -11,10 +12,14 @@ public partial class Keyword
public int Id { get; set; }
[Column(Order = 2)]
public LocaleString Name = [];
[Column(Order = 3)]
[MaxLength(Pf2eDbContext.NameLength)]
public string Slug { get; set; } = null!;
[Newtonsoft.Json.JsonIgnore]
public virtual ICollection<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;
public partial class KeywordAlias
public class KeywordAlias
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
@@ -18,5 +18,5 @@ public partial class KeywordAlias
public string Value { get; set; } = null!;
[Newtonsoft.Json.JsonIgnore]
public virtual Keyword Keyword { get; set; } = null!;
public Keyword Keyword { get; set; } = null!;
}
+3 -3
View File
@@ -5,7 +5,7 @@ using Microsoft.EntityFrameworkCore;
namespace Pf2eModel.Model.Base.Source;
[PrimaryKey(nameof(EntityId), nameof(SourceId))]
public partial class EntitySource
public class EntitySource
{
[Column(Order = 1)]
public int EntityId { get; set; }
@@ -23,7 +23,7 @@ public partial class EntitySource
public bool IsRemaster { get; set; }
[Newtonsoft.Json.JsonIgnore]
public virtual Entity Entity { get; set; } = null!;
public Entity Entity { get; set; } = null!;
public virtual Source Source { get; set; } = null!;
public Source Source { get; set; } = null!;
}
+1 -1
View File
@@ -4,7 +4,7 @@ using Pf2eModel.Model.Enum;
namespace Pf2eModel.Model.Base.Source;
public partial class Source
public class Source
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
@@ -5,7 +5,7 @@ using Microsoft.EntityFrameworkCore;
namespace Pf2eModel.Model.Base.Targets;
[PrimaryKey(nameof(EntityId), nameof(TargetId))]
public partial class EntityExternal
public class EntityExternal
{
[Column(Order = 0)]
public int EntityId { get; set; }
@@ -3,7 +3,7 @@ using System.ComponentModel.DataAnnotations.Schema;
namespace Pf2eModel.Model.Base.Targets;
public partial class ExternalTarget
public class ExternalTarget
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
@@ -24,5 +24,5 @@ public partial class ExternalTarget
[Column(Order = 4)]
[Newtonsoft.Json.JsonIgnore]
public virtual ICollection<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;
public partial class Frequency
public class Frequency
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
@@ -18,17 +18,19 @@ public static class LocaleExtension
public static LocaleType GetTypeByString(this string localeType)
{
foreach (System.Reflection.FieldInfo field in typeof(LocaleType).GetFields())
foreach (FieldInfo field in typeof(LocaleType).GetFields())
{
if (
Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute))
is DescriptionAttribute descriptionAttribute
is not DescriptionAttribute descriptionAttribute
)
{
if (descriptionAttribute.Description == localeType)
{
return (LocaleType)field.GetValue(null)!;
}
continue;
}
if (descriptionAttribute.Description == localeType)
{
return (LocaleType)field.GetValue(null)!;
}
}
+1 -1
View File
@@ -4,7 +4,7 @@ using Pf2eModel.Model.Enum;
namespace Pf2eModel.Model.Common;
public partial class Training
public class Training
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+2 -1
View File
@@ -5,7 +5,7 @@ using Pf2eModel.Model.Enum;
namespace Pf2eModel.Model.Structure;
[EntityOfType(EntityType.Action)]
public partial class Action : IEntity
public class Action : IEntity
{
[System.ComponentModel.DataAnnotations.Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
@@ -28,6 +28,7 @@ public partial class Action : IEntity
public bool IsPassive { get; set; } = false;
[Column(Order = 6)]
// ReSharper disable once EntityFramework.ModelValidation.UnlimitedStringLength
public string? Extra { get; set; }
public Entity Entity { get; set; } = null!;
+5 -5
View File
@@ -6,7 +6,7 @@ using Pf2eModel.Model.Structure.AncestryRelation;
namespace Pf2eModel.Model.Structure;
[EntityOfType(EntityType.Ancestry)]
public partial class Ancestry : IEntity
public class Ancestry : IEntity
{
[System.ComponentModel.DataAnnotations.Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
@@ -27,11 +27,11 @@ public partial class Ancestry : IEntity
[Column(Order = 5)]
public AncestryVision Vision { get; set; } = AncestryVision.Normal;
public virtual ICollection<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;
public partial class AncestryBoost
public class AncestryBoost
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
@@ -21,5 +21,5 @@ public partial class AncestryBoost
public Enum.AttributeValues Value { get; set; }
[JsonIgnore]
public virtual Ancestry Ancestry { get; set; } = null!;
public Ancestry Ancestry { get; set; } = null!;
}
@@ -5,7 +5,7 @@ using Pf2eModel.Model.Base;
namespace Pf2eModel.Model.Structure.AncestryRelation;
[PrimaryKey(nameof(AncestryId), nameof(ItemId))]
public partial class AncestryItem
public class AncestryItem
{
[Column(Order = 1)]
public int AncestryId { get; set; }
@@ -16,7 +16,7 @@ public partial class AncestryItem
[Column(Order = 3)]
public int Level { get; set; }
public virtual Ancestry Ancestry { get; set; } = null!;
public Ancestry Ancestry { get; set; } = null!;
public virtual Entity Item { get; set; } = null!;
public Entity Item { get; set; } = null!;
}
@@ -4,7 +4,7 @@ using Microsoft.EntityFrameworkCore;
namespace Pf2eModel.Model.Structure.AncestryRelation;
[PrimaryKey(nameof(AncestryId), nameof(LanguageId))]
public partial class AncestryLanguage
public class AncestryLanguage
{
[Column(Order = 1)]
public int AncestryId { get; set; }
@@ -15,7 +15,7 @@ public partial class AncestryLanguage
[Column(Order = 3)]
public bool IsAdditional { get; set; }
public virtual Ancestry Ancestry { get; set; } = null!;
public Ancestry Ancestry { get; set; } = null!;
public virtual Language Language { get; set; } = null!;
public Language Language { get; set; } = null!;
}
+2 -1
View File
@@ -21,9 +21,10 @@ public class Archetype : IEntity
public int? DedicationFeatId { get; set; } = null;
[Column(Order = 4)]
// ReSharper disable once EntityFramework.ModelValidation.UnlimitedStringLength
public string? Extra { get; set; }
public Feat DedicationFeat { get; set; } = null!;
public virtual ICollection<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;
[EntityOfType(EntityType.Background)]
public partial class Background : IEntity
public class Background : IEntity
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
@@ -16,9 +16,9 @@ public partial class Background : IEntity
public Entity Entity { get; set; } = null!;
public virtual ICollection<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;
public partial class BackgroundBoost
public class BackgroundBoost
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
@@ -19,5 +19,5 @@ public partial class BackgroundBoost
public AttributeValues Attribute { get; set; }
[JsonIgnore]
public virtual Background Background { get; set; } = null!;
public Background Background { get; set; } = null!;
}
@@ -6,7 +6,7 @@ using Pf2eModel.Model.Base;
namespace Pf2eModel.Model.Structure.BackgroundRelation;
[PrimaryKey(nameof(BackgroundId), nameof(EntityId))]
public partial class BackgroundItem
public class BackgroundItem
{
[Column(Order = 1)]
public int BackgroundId { get; set; }
@@ -18,7 +18,7 @@ public partial class BackgroundItem
public int? Level { get; set; }
[JsonIgnore]
public virtual Background Background { get; set; } = null!;
public Background Background { get; set; } = null!;
public virtual Entity Entity { get; set; } = null!;
public Entity Entity { get; set; } = null!;
}
@@ -6,7 +6,7 @@ using Pf2eModel.Model.Enum;
namespace Pf2eModel.Model.Structure.BackgroundRelation;
public partial class BackgroundTraining
public class BackgroundTraining
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
@@ -23,7 +23,7 @@ public partial class BackgroundTraining
public TrainingStage Level { get; set; }
[JsonIgnore]
public virtual Background Background { get; set; } = null!;
public Background Background { get; set; } = null!;
public virtual Training Training { get; set; } = null!;
public Training Training { get; set; } = null!;
}
@@ -5,7 +5,7 @@ using Pf2eModel.Model.Enum;
namespace Pf2eModel.Model.Structure.ClassRelation;
public partial class ClassKeyAbility
public class ClassKeyAbility
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
@@ -19,5 +19,5 @@ public partial class ClassKeyAbility
public AttributeValues Value { get; set; }
[JsonIgnore]
public virtual Class Class { get; set; } = null!;
public Class Class { get; set; } = null!;
}
@@ -6,7 +6,7 @@ using Pf2eModel.Model.Enum;
namespace Pf2eModel.Model.Structure.ClassRelation;
public partial class ClassProgression
public class ClassProgression
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
@@ -26,7 +26,7 @@ public partial class ClassProgression
public int? EntityId { get; set; }
[JsonIgnore]
public virtual Class Class { get; set; } = null!;
public Class Class { get; set; } = null!;
public virtual Entity? Entity { get; set; }
public Entity? Entity { get; set; }
}
@@ -6,7 +6,7 @@ using Pf2eModel.Model.Enum;
namespace Pf2eModel.Model.Structure.ClassRelation;
public partial class ClassTraining
public class ClassTraining
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
@@ -27,7 +27,7 @@ public partial class ClassTraining
public TrainingStage Stage { get; set; }
[JsonIgnore]
public virtual Class Class { get; set; } = null!;
public Class Class { get; set; } = null!;
public virtual Training? Training { get; set; }
public Training? Training { get; set; }
}
+2 -1
View File
@@ -6,7 +6,7 @@ using Pf2eModel.Model.Enum;
namespace Pf2eModel.Model.Structure;
[EntityOfType(EntityType.Condition)]
public partial class Condition : IEntity
public class Condition : IEntity
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
@@ -19,5 +19,6 @@ public partial class Condition : IEntity
public ConditionGroup? Group { get; set; }
[Column(Order = 3)]
// ReSharper disable once EntityFramework.ModelValidation.UnlimitedStringLength
public string? Extra { get; set; }
}
+6 -7
View File
@@ -8,7 +8,7 @@ using Pf2eModel.Model.Structure.DeityRelation;
namespace Pf2eModel.Model.Structure;
[EntityOfType(EntityType.Deity)]
public partial class Deity : IEntity
public class Deity : IEntity
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
@@ -29,14 +29,13 @@ public partial class Deity : IEntity
[Column(Order = 5)]
public bool SanctificationMust { get; set; }
public virtual ICollection<DeityAttribute> DeityAttributes { get; set; } =
new List<DeityAttribute>();
public ICollection<DeityAttribute> DeityAttributes { get; set; } = 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;
[PrimaryKey(nameof(DeityId), nameof(Attribute))]
public partial class DeityAttribute
public class DeityAttribute
{
[Column(Order = 1)]
public int DeityId { get; set; }
@@ -13,5 +13,5 @@ public partial class DeityAttribute
[Column(Order = 2)]
public AttributeValues Attribute { get; set; }
public virtual Deity Deity { get; set; } = null!;
public Deity Deity { get; set; } = null!;
}
@@ -3,7 +3,7 @@ using System.ComponentModel.DataAnnotations.Schema;
namespace Pf2eModel.Model.Structure.DeityRelation;
public partial class DeityDomain
public class DeityDomain
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
@@ -3,7 +3,7 @@ using System.ComponentModel.DataAnnotations.Schema;
namespace Pf2eModel.Model.Structure.DeityRelation;
public partial class DeitySpell
public class DeitySpell
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
@@ -3,7 +3,7 @@ using System.ComponentModel.DataAnnotations.Schema;
namespace Pf2eModel.Model.Structure.DeityRelation;
public partial class DeityWeapon
public class DeityWeapon
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+4 -3
View File
@@ -7,7 +7,7 @@ using Pf2eModel.Model.Structure.FeatRelation;
namespace Pf2eModel.Model.Structure;
[EntityOfType(EntityType.Feat)]
public partial class Feat : IEntity
public class Feat : IEntity
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
@@ -38,9 +38,10 @@ public partial class Feat : IEntity
public bool IsPassive { get; set; } = false;
[Column(Order = 10)]
// ReSharper disable once EntityFramework.ModelValidation.UnlimitedStringLength
public string? Extra { get; set; }
public virtual ICollection<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;
public partial class FeatPrerequisite
public class FeatPrerequisite
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+1 -1
View File
@@ -6,7 +6,7 @@ using Pf2eModel.Model.Enum;
namespace Pf2eModel.Model.Structure;
[EntityOfType(EntityType.Heritage)]
public partial class Heritage : IEntity
public class Heritage : IEntity
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
+1 -1
View File
@@ -6,7 +6,7 @@ using Pf2eModel.Model.Enum;
namespace Pf2eModel.Model.Structure;
[EntityOfType(EntityType.Language)]
public partial class Language : IEntity
public class Language : IEntity
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+1 -1
View File
@@ -7,7 +7,7 @@ using Pf2eModel.Model.Structure.SpellRelation;
namespace Pf2eModel.Model.Structure;
[EntityOfType(EntityType.Spell)]
public partial class Spell : IEntity
public class Spell : IEntity
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
@@ -4,7 +4,7 @@ using Pf2eModel.Model.Enum;
namespace Pf2eModel.Model.Structure.SpellRelation;
public partial class SpellArea
public class SpellArea
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
@@ -4,7 +4,7 @@ using Pf2eModel.Model.Enum;
namespace Pf2eModel.Model.Structure.SpellRelation;
public partial class SpellDamage
public class SpellDamage
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
@@ -3,7 +3,7 @@ using System.ComponentModel.DataAnnotations.Schema;
namespace Pf2eModel.Model.Structure.SpellRelation;
public partial class SpellDefense
public class SpellDefense
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
@@ -3,7 +3,7 @@ using System.ComponentModel.DataAnnotations.Schema;
namespace Pf2eModel.Model.Structure.SpellRelation;
public partial class SpellRitual
public class SpellRitual
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
+3 -1
View File
@@ -21,7 +21,9 @@ public class Subclass : IEntity
public SubclassGroup Group { get; set; } = null!;
[Column(Order = 3)]
public LocaleString Additional { get; set; } = new();
public LocaleString Additional { get; set; } = [];
[Column(Order = 4)]
// ReSharper disable once EntityFramework.ModelValidation.UnlimitedStringLength
public string Extra { get; set; } = "{}";
}
+2 -8
View File
@@ -1,13 +1,7 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Pf2eModel.Model.Base;
using Pf2eModel.Model.Enum;
using Pf2eModel.Model.Structure.ClassRelation;
namespace Pf2eModel.Model.Structure;
@@ -25,5 +19,5 @@ public class SubclassGroup : IEntity
public Class? Class { get; set; } = null!;
public virtual ICollection<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;
[EntityOfType(EntityType.Trait)]
public partial class Trait : IEntity
public class Trait : IEntity
{
[Key]
[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 Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
@@ -21,6 +22,7 @@ using Pf2eModel.Model.Structure.SpellRelation;
namespace Pf2eModel;
[SuppressMessage("ReSharper", "UnusedParameter.Local")]
public partial class Pf2eDbContext
{
public const int NameLength = 255;
@@ -39,11 +41,7 @@ public partial class Pf2eDbContext
private void BindEntity<T>(EntityTypeBuilder<T> entity)
where T : class, IEntity
{
entity
.HasOne(entity => entity.Entity)
.WithOne()
.HasForeignKey<T>(entity => entity.Id)
.IsRequired();
entity.HasOne(e => e.Entity).WithOne().HasForeignKey<T>(e => e.Id).IsRequired();
}
private void BindLocaleString<T>(
@@ -144,6 +142,8 @@ public partial class Pf2eDbContext
// Keywords
modelBuilder.Entity<Keyword>(entity =>
{
BindLocaleString(entity, k => k.Name);
entity
.HasMany(e => e.Aliases)
.WithOne(e => e.Keyword)
@@ -483,9 +483,11 @@ public partial class Pf2eDbContext
);
}
// ReSharper disable once InconsistentNaming
public static bool LocaleSearch(LocaleString data, string search_value) =>
throw new InvalidOperationException("Only for LINQ translation");
// ReSharper disable once InconsistentNaming
public static bool LocaleSearchArray(LocaleString data, IEnumerable<string> search_values) =>
throw new InvalidOperationException("Only for LINQ translation");
}
+62 -42
View File
@@ -1,7 +1,5 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Pf2eModel;
using Pf2eModel.Model;
using Pf2eModel.Model.Base;
@@ -39,64 +37,86 @@ internal class EntityConverter : JsonConverter
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
{
Dictionary<string, object?> dictionary = DictionarySerialization(value!);
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);
}
switch (value)
else
{
case Entity entity:
ProcessEntity(dictionary, entity);
break;
case Trait trait:
ProcessTrait(dictionary, trait);
break;
dictionary.Remove("Description");
}
serializer.Serialize(writer, dictionary);
}
/*
writer.WriteStartObject();
private abstract class Processor
{
public abstract Type Type { get; }
foreach (KeyValuePair<string, object> keyValuePair in dictionary)
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
)
{
writer.WritePropertyName(keyValuePair.Key, true);
serializer.Serialize(writer, keyValuePair.Value);
method.Invoke(dictionary, (T)entity, converter);
}
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()!;
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());
}
private void ProcessEntity(Dictionary<string, object?> dictionary, Entity entity)
{
dictionary.Remove("Description");
}
private void ProcessSubclass(Dictionary<string, object?> dictionary, Subclass entity)
{
dictionary.Remove("Group");
}
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(
public override object ReadJson(
JsonReader reader,
Type objectType,
object? existingValue,
@@ -27,7 +27,7 @@ public class LocaleStringConverter : JsonConverter
public override bool CanRead => false;
public override object? ReadJson(
public override object ReadJson(
JsonReader reader,
Type objectType,
object? existingValue,
+17 -8
View File
@@ -1,15 +1,22 @@
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.ComponentModel;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Xml.Linq;
using Microsoft.Win32;
#pragma warning disable CS8603 // Possible null reference return.
#pragma warning disable CS0184 // 'is' expression's given expression is never of the provided type
#pragma warning disable CA1806
#pragma warning disable CS8605 // Unboxing a possibly null value.
#pragma warning disable CS8622 // Nullability of reference types in type of parameter doesn't match the target delegate (possibly because of nullability attributes).
#pragma warning disable CS8604 // Possible null reference argument.
#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type.
#pragma warning disable CS8602 // Dereference of a possibly null reference.
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
#pragma warning disable CS0169 // Field is never used
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
// ReSharper disable All
namespace Pf2eViewer;
@@ -1088,8 +1095,9 @@ public class DarkModeCS
/// <summary>Returns Windows's System Colors for UI components following Google Material Design concepts.</summary>
/// <param name="Window">[OPTIONAL] Applies DarkMode (if set) to this Window Title and Background.</param>
/// <param name="ColorMode">O: DarkMode, 1: LightMode</param>
/// <returns>List of Colors: Background, OnBackground, Surface, OnSurface, Primary, OnPrimary, Secondary, OnSecondary</returns>
public static OSThemeColors GetSystemColors(Form Window = null, int ColorMode = 0) //<- O: DarkMode, 1: LightMode
public static OSThemeColors GetSystemColors(Form Window = null, int ColorMode = 0)
{
OSThemeColors _ret = new OSThemeColors();
@@ -1287,6 +1295,7 @@ public class DarkModeCS
/// <summary>Attemps to apply Window's Dark Style to the Control and all its childs.</summary>
/// <param name="control"></param>
/// <param name="IsDarkMode"></param>
private static void ApplySystemDarkTheme(Control control = null, bool IsDarkMode = true)
{
/*
+171
View File
@@ -0,0 +1,171 @@
using LinqToDB.SqlQuery;
using LinqToDB.Tools;
using Microsoft.EntityFrameworkCore;
using Pf2eModel;
using Pf2eModel.Model;
using Pf2eModel.Model.Base;
using Pf2eModel.Model.Enum;
using Pf2eModel.Model.Structure;
namespace Pf2eViewer;
public class DatabaseViewer
{
public readonly Pf2eDbContext Context = new();
private int _perPage = 30;
private int _page = 1;
private EntityType _entityType;
public int PerPage
{
get => _perPage;
set
{
_perPage = value;
UpdateView();
}
}
public int Page
{
get => _page;
set
{
_page = value;
UpdateView();
}
}
public EntityType EntityType
{
get => _entityType;
set
{
_entityType = value;
UpdateView();
}
}
public int Count { get; private set; }
public int MaxPage => Convert.ToInt32(Math.Ceiling(1.0 * Count / PerPage));
public ICollection<Entity> Entities { get; private set; } = [];
public delegate void ViewUpdateHandler();
public event ViewUpdateHandler? ViewUpdateStart;
public event ViewUpdateHandler? ViewUpdate;
public Entity? LastEntity;
public IEntity? LastSubEntity;
public delegate void ViewSelectHandler(Entity? entity, IEntity? subEntity);
public event ViewSelectHandler? ViewSelect;
public void UpdateView()
{
ViewUpdateStart?.Invoke();
Count = Context.Entities.Count(e => e.Type == EntityType);
if (_page < 1 || _page > MaxPage)
{
_page = Math.Min(MaxPage, Math.Max(1, _page));
}
Entities = Context
.Entities.Where(e => e.Type == EntityType)
.OrderBy(e => e.Id)
.Skip((Page - 1) * PerPage)
.Take(PerPage)
.ToList();
ViewUpdate?.Invoke();
}
public void Select(Entity entity)
{
Getter? getter = _getters.FirstOrDefault(g => g.Type == entity.Type);
Entity? fullEntity = Context
.Entities.Where(e => e.Id == entity.Id && e.Type == entity.Type)
.Include(e => e.Sources)
.ThenInclude(s => s.Source)
.Include(e => e.Keywords)
.Include(e => e.Externals)
.ThenInclude(x => x.Target)
.Include(e => e.Traits)
.ThenInclude(t => t.Entity)
.FirstOrDefault();
IEntity? subEntity =
(getter != null)
? getter.Get(entity.Id, Context)
: (IEntity?)Context.Find(EntityOfTypeAttribute.Resolve(entity), entity.Id);
ViewSelect?.Invoke(fullEntity, subEntity);
LastEntity = fullEntity;
LastSubEntity = subEntity;
}
private readonly ICollection<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);
}
}
}
+79 -226
View File
@@ -1,7 +1,7 @@
using Microsoft.EntityFrameworkCore;
using System.Windows.Forms;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using Pf2eModel;
using Pf2eModel.Model;
@@ -9,6 +9,7 @@ using Pf2eModel.Model.Base;
using Pf2eModel.Model.Common.I18n;
using Pf2eModel.Model.Enum;
using Pf2eViewer.Converters;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;
namespace Pf2eViewer;
@@ -16,15 +17,15 @@ public partial class MainViewForm : Form
{
public Pf2eDbContext Context = new();
public string JsonViewerHtml = string.Empty;
public readonly string JsonViewerHtml;
private DarkModeCS dm = null;
public readonly DatabaseViewer Viewer = new();
public MainViewForm()
{
InitializeComponent();
dm = new DarkModeCS(this) { ColorMode = DarkModeCS.DisplayMode.SystemDefault };
_ = new DarkModeCS(this) { ColorMode = DarkModeCS.DisplayMode.SystemDefault };
Reset();
@@ -32,6 +33,10 @@ public partial class MainViewForm : Form
using StreamReader reader = new("Resources/json-viewer.html");
JsonViewerHtml = reader.ReadToEnd();
Viewer.ViewUpdateStart += Viewer_ViewUpdateStart;
Viewer.ViewUpdate += Viewer_ViewUpdate;
Viewer.ViewSelect += Viewer_ViewSelect;
}
public void Reset()
@@ -43,53 +48,63 @@ public partial class MainViewForm : Form
TypesListBox.SelectedIndex = 0;
}
public const int PerPage = 30;
public int Count;
public int Page
private void ToggleFreeze(bool state)
{
get => Convert.ToInt32(PageTextBox.Text);
set
{
PageTextBox.Text = value.ToString();
UpdateTreeView();
}
EntityGridView.Visible = state;
JsonWebView.Visible = state;
FirstPageButton.Enabled = state;
PrevPageButton.Enabled = state;
NextPageButton.Enabled = state;
LastPageButton.Enabled = state;
PageTextBox.Enabled = state;
}
public int MaxPage => Convert.ToInt32(Math.Ceiling(1.0 * Count / PerPage));
// Viewer events
private void UpdateTreeView()
private void Viewer_ViewUpdateStart()
{
// EntityTreeView.Nodes.Clear();
EntityType type = (EntityType)TypesListBox.SelectedItem!;
Count = Context.Entities.Count(e => e.Type == type);
if (Page < 1 || Page > MaxPage)
{
Page = Math.Min(MaxPage, Math.Max(1, Page));
return;
}
EntityGridView.DataSource = Context
.Entities.Where(e => e.Type == (EntityType)TypesListBox.SelectedItem)
.OrderBy(e => e.Id)
.Select(e => new EntityGridRow(e))
.Skip((Page - 1) * PerPage)
.Take(PerPage)
.ToList();
EntityGridView.Rows[0].Selected = true;
PaginationLabel.Text = $"Всего: {Count} объектов, {MaxPage} страниц";
FirstPageButton.Enabled = Page > 1;
PrevPageButton.Enabled = Page > 1;
NextPageButton.Enabled = Page < MaxPage;
LastPageButton.Enabled = Page < MaxPage;
ToggleFreeze(false);
}
private void Viewer_ViewUpdate()
{
EntityGridView.DataSource = Viewer.Entities.Select(e => new EntityGridRow(e));
PaginationLabel.Text = $@"Всего: {Viewer.Count} объектов, {Viewer.MaxPage} страниц";
FirstPageButton.Enabled = Viewer.Page > 1;
PrevPageButton.Enabled = Viewer.Page > 1;
NextPageButton.Enabled = Viewer.Page < Viewer.MaxPage;
LastPageButton.Enabled = Viewer.Page < Viewer.MaxPage;
ToggleFreeze(true);
}
private void Viewer_ViewSelect(Entity? entity, IEntity? subEntity)
{
JsonSerializerSettings settings = new()
{
Converters =
[
new StringEnumConverter { NamingStrategy = new CamelCaseNamingStrategy() },
new EntityConverter(),
new LocaleStringConverter(),
],
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
};
string result = JsonConvert.SerializeObject(entity, settings);
string subResult = JsonConvert.SerializeObject(subEntity, settings);
string json = result[..^1] + $",\"SubEntity\":{subResult}}}";
RenderJson(json);
}
// Form events
private void TypesListBox_SelectedIndexChanged(object sender, EventArgs _)
{
if (TypesListBox.SelectedItem is null)
@@ -97,27 +112,27 @@ public partial class MainViewForm : Form
return;
}
Page = 1;
Viewer.Page = 1;
}
private void FirstPageButton_Click(object sender, EventArgs e)
{
Page = 1;
Viewer.Page = 1;
}
private void PrevPageButton_Click(object sender, EventArgs e)
{
Page--;
Viewer.Page--;
}
private void NextPageButton_Click(object sender, EventArgs e)
{
Page++;
Viewer.Page++;
}
private void LastPageButton_Click(object sender, EventArgs e)
{
Page = MaxPage;
Viewer.Page = Viewer.MaxPage;
}
private void PageTextBox_KeyPress(object sender, KeyPressEventArgs e)
@@ -129,7 +144,7 @@ public partial class MainViewForm : Form
if (e.KeyChar == (char)Keys.Return)
{
UpdateTreeView();
Viewer.Page = Convert.ToInt32(PageTextBox.Text);
}
}
@@ -148,37 +163,7 @@ public partial class MainViewForm : Form
EntityGridRow data = (EntityGridRow)row.DataBoundItem;
ViewEntity(data.Id);
}
public void ViewEntity(int id)
{
// EntityTreeView.Nodes.Clear();
Entity? entity = Context
.Entities.Where(e => e.Id == id && e.Type == (EntityType)TypesListBox.SelectedItem!)
.Include(e => e.Sources)
.ThenInclude(s => s.Source)
.Include(e => e.Keywords)
.Include(e => e.Externals)
.ThenInclude(x => x.Target)
.Include(e => e.Traits)
.ThenInclude(t => t.Entity)
.FirstOrDefault();
if (entity == null)
{
DescriptionTextBox.Text = "Not found";
return;
}
string json = SerializeEntity(entity);
// JsonToTreeview(EntityTreeView, json, "Entity");
RenderJson(json);
DescriptionTextBox.Text = entity.Description.Replace("\\r", "").Replace("\\n", "\\r\\n");
Viewer.Select(data.GetEntity());
}
public void RenderJson(string json)
@@ -199,149 +184,17 @@ public partial class MainViewForm : Form
)
{
JsonWebView.Visible = true;
DataGridViewRow row = EntityGridView.SelectedRows[0];
EntityGridRow data = (EntityGridRow)row.DataBoundItem;
ViewEntity(data.Id);
}
public string SerializeEntity(Entity entity)
{
JsonSerializerSettings settings = new()
{
Converters =
[
new StringEnumConverter { NamingStrategy = new CamelCaseNamingStrategy() },
new EntityConverter(),
new LocaleStringConverter(),
],
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
};
string result = JsonConvert.SerializeObject(entity, settings);
Type type = EntityOfTypeAttribute.Resolve(entity);
string subResult = JsonConvert.SerializeObject(ResolveSubEntity(entity), settings);
return result[..^1] + $",\"SubEntity\":{subResult}}}";
}
public IEntity? ResolveSubEntity(Entity entity)
{
switch (entity.Type)
{
case EntityType.Ancestry:
return Context
.Ancestries.Include(a => a.AncestryBoosts)
.FirstOrDefault(e => e.Id == entity.Id);
case EntityType.Archetype:
return Context
.Archetypes.Include(a => a.DedicationFeat)
.ThenInclude(f => f.Entity)
.Include(a => a.Feats)
.ThenInclude(f => f.Entity)
.FirstOrDefault(a => a.Id == entity.Id);
case EntityType.Background:
return Context
.Backgrounds.Include(b => b.BackgroundBoosts)
.Include(b => b.BackgroundSkills)
.ThenInclude(t => t.Training)
.Include(b => b.BackgroundItems)
.FirstOrDefault(e => e.Id == entity.Id);
case EntityType.Class:
return Context
.Classes.Include(c => c.ClassKeyAbilities)
.Include(c => c.ClassTrainings)
.ThenInclude(t => t.Training)
.Include(c => c.ClassProgressions)
.FirstOrDefault(c => c.Id == entity.Id);
case EntityType.Feat:
return Context
.Feats.Include(f => f.Archetypes)
.ThenInclude(a => a.Entity)
.FirstOrDefault(f => f.Id == entity.Id);
default:
Type type = EntityOfTypeAttribute.Resolve(entity);
return (IEntity?)Context.Find(type, entity.Id);
}
}
public static void JsonToTreeview(TreeView treeView, string jsonString, string rootName)
{
object json = jsonString.StartsWith('[')
? JArray.Parse(jsonString)
: JObject.Parse(jsonString);
treeView.Nodes.Clear();
foreach (TreeNode node in Element2TreeNode(json, rootName).Nodes)
{
treeView.Nodes.Add(node);
}
treeView.ExpandAll();
}
private static TreeNode Element2TreeNode(object element, string text = "")
{
List<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;
Viewer_ViewSelect(Viewer.LastEntity, Viewer.LastSubEntity);
}
}
internal class EntityGridRow(Entity e)
{
public int Id { get; set; } = e.Id;
public string Name { get; set; } = e.Name[LocaleType.English];
public EntityRarity Rarity { get; set; } = e.Rarity;
private readonly Entity _e = e;
public Entity GetEntity() => _e;
}