Compare commits

..
6 Commits
18 changed files with 508 additions and 92 deletions
+1 -1
View File
@@ -121,7 +121,7 @@ csharp_style_conditional_delegate_call = true
# Предпочтения модификатора
csharp_prefer_static_anonymous_function = true
csharp_prefer_static_local_function = true
csharp_preferred_modifier_order = public, private, protected, readonly, file, static, extern, internal, virtual, abstract, override, sealed, new, unsafe, required, volatile, async:suggestion
csharp_preferred_modifier_order = public, private, protected, file, static, extern, internal, virtual, abstract, override, sealed, readonly, new, unsafe, required, volatile, async:suggestion
csharp_style_prefer_readonly_struct = true
csharp_style_prefer_readonly_struct_member = true
+12 -6
View File
@@ -3,29 +3,35 @@
namespace Pf2eDataScrapper.Importers;
/// <summary>
/// Абстрактный класс импортёра
/// Абстрактный класс импортёра.
/// </summary>
internal abstract class AbstractImporter
{
/// <summary>
/// Отображаемое имя импортёра
/// Отображаемое имя импортёра.
/// </summary>
public abstract string Name { get; }
/// <summary>
/// Короткое имя импортёра
/// Короткое имя импортёра.
/// </summary>
public abstract string Slug { get; }
public abstract AbstractImporterRepository Repository { get; }
/// <summary>
/// Путь к директории с данными для импортёра
/// Путь к директории с данными для импортёра.
/// </summary>
public string Dir => Path.Combine(EnvSettings.DataDirPath, Slug);
/// <summary>
/// Метод подготовки данных для импорта
/// Перечисление процессоров, которые будут использоваться для обработки данных.
/// При пустом массиве будут обработаны все доступные процессоры.
/// </summary>
public string[] ProcessorNames { get; set; } = [];
/// <summary>
/// Метод подготовки данных для импорта.
/// </summary>
public abstract void Prepare();
@@ -35,7 +41,7 @@ internal abstract class AbstractImporter
public abstract void Import();
/// <summary>
/// Построение схем данных
/// Построение схем данных.
/// </summary>
public abstract void BuildSchemas();
}
@@ -37,6 +37,7 @@ internal class AonprdImporter : AbstractImporter
public override void Import()
{
AonprdDataBuilder builder = new(Loader);
builder.Processors.UseOnlyNames(ProcessorNames);
builder.TruncateTables();
builder.Build();
@@ -14,7 +14,9 @@ internal class AonprdProcessorCollection
public int Priority = -1;
}
public readonly List<ProcessorItem> Items = [];
private readonly List<ProcessorItem> _allItems;
public List<ProcessorItem> Items { get; private set; } = [];
public AonprdProcessorCollection()
{
@@ -50,6 +52,8 @@ internal class AonprdProcessorCollection
Items.Add(new(attribute, constructor));
}
_allItems = Items.ToList();
}
private void CheckDependencies()
@@ -109,4 +113,28 @@ internal class AonprdProcessorCollection
yield return item;
}
}
public void UseOnlyNames(string[] processorNames)
{
Dictionary<string, bool> nameFlags = processorNames.ToDictionary(name => name, _ => false);
while (nameFlags.Values.Any(v => !v))
{
foreach (
KeyValuePair<string, bool> pair in nameFlags.Where(pair => !pair.Value).ToList()
)
{
ProcessorItem item = _allItems.First(item => item.Attribute.Name == pair.Key);
foreach (string dependency in item.Attribute.Dependencies)
{
nameFlags.TryAdd(dependency, false);
}
nameFlags[pair.Key] = true;
}
}
Items = _allItems.Where(item => nameFlags.ContainsKey(item.Attribute.Name)).ToList();
}
}
@@ -21,7 +21,7 @@ internal partial class StringHelper
{
MarkdownLinkSplit result = new();
Match match = MarkdownRegex().Match(input);
Match match = LinkRegex().Match(input);
if (match.Captures.Count > 0)
{
result.Links.Add(
@@ -34,7 +34,7 @@ internal partial class StringHelper
);
}
result.Suffix = MarkdownRegex().Replace(input, "");
result.Suffix = LinkRegex().Replace(input, "");
return result;
}
@@ -51,7 +51,7 @@ internal partial class StringHelper
public static partial Regex SourceSuffixRegex();
[GeneratedRegex(@"(.*?)\[(.*?)\]\((.*?)\)")]
public static partial Regex MarkdownRegex();
public static partial Regex LinkRegex();
}
internal class MarkdownLinkSplit
@@ -0,0 +1,71 @@
using Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Helpers;
using Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors;
using Pf2eModel.Model.Base;
using Pf2eModel.Model.Base.Source;
using Pf2eModel.Model.Common.I18n;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder;
internal class LegacyEntityHandler
{
public string LegacyId { get; private set; }
public string LegacyName { get; private set; } = "";
public List<EntitySource> LegacySources { get; private set; } = [];
public bool LegacyPrepared { get; private set; } = false;
public List<Entity> RemasterEntities = [];
public LegacyEntityHandler(AbstractEntityProcessor processor)
{
LegacyId = processor.Input.GetRequired<string>("id");
Prepare(processor);
}
public LegacyEntityHandler(string legacyId, Entity remasterEntity)
{
LegacyId = legacyId;
LegacyPrepared = false;
RemasterEntities.Add(remasterEntity);
}
public void Prepare(AbstractEntityProcessor processor)
{
if (LegacyPrepared)
{
return;
}
LegacyName = processor.Input.GetRequired<string>("name");
LegacySources = processor.GetSources();
LegacyPrepared = true;
foreach (Entity remasterEntity in RemasterEntities)
{
Apply(remasterEntity);
}
}
public void Apply(Entity remasterEntity)
{
if (!remasterEntity.Name.Contains(LegacyName))
{
remasterEntity.Name[LocaleType.LegacyEnglish] = LegacyName;
}
foreach (EntitySource legacySource in LegacySources)
{
EntitySource entitySource = new()
{
Entity = remasterEntity,
Source = legacySource.Source,
Suffix = legacySource.Suffix,
IsRemaster = false,
};
remasterEntity.Sources.Add(entitySource);
}
}
}
@@ -29,18 +29,17 @@ internal abstract class AbstractEntityProcessor(AonprdProcessorInput input)
{
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);
LegacyEntityHandler handler = new(this);
LegacyHandlers.Add(handler);
throw new LegacyItemException(handler.LegacyName);
}
AddEntity();
AddAlias();
AddSource();
AddExternal();
AddTraits();
ProcessLegacy();
}
public override void Save()
@@ -49,6 +48,8 @@ internal abstract class AbstractEntityProcessor(AonprdProcessorInput input)
Context.SaveChanges();
}
// Description generation
protected virtual string[] AddedDescriptionPaths() => [];
protected string AddedDescription(string path) =>
@@ -60,6 +61,8 @@ internal abstract class AbstractEntityProcessor(AonprdProcessorInput input)
protected virtual string GetDescription() =>
Input.GetRequired<string>("markdown") + AddedDescription(AddedDescriptionPaths());
// Entity generation
protected virtual void AddEntity()
{
Entity.Id = Helper.NextEntityId;
@@ -78,57 +81,12 @@ internal abstract class AbstractEntityProcessor(AonprdProcessorInput input)
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)
foreach (EntitySource entitySource in GetSources())
{
source = new()
{
Name = name,
Category = Input.EnumRequired<SourceCategory>("primary_source_category"),
ReleaseDate = DateOnly.Parse(Input.GetRequired<string>("release_date")),
};
Context.Sources.Add(source);
Entity.Sources.Add(entitySource);
}
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()
@@ -179,20 +137,76 @@ internal abstract class AbstractEntityProcessor(AonprdProcessorInput input)
}
}
protected void ProcessLegacy(string id, string name)
protected virtual void ProcessLegacy()
{
if (RemasterWithoutLegacy.TryGetValue(id, out Entity? remastered))
{
// if (remastered.ToString() != name)
remastered.Name[LocaleType.LegacyEnglish] = name;
JArray? legacyIdArray = Input.GetOptional<JArray>("legacy_id");
RemasterWithoutLegacy.Remove(id);
if (legacyIdArray is null)
{
return;
}
string[] legacyIds = legacyIdArray.ToArray().Select(t => t.ToString()).ToArray();
if (legacyIds.Length == 0)
{
return;
}
foreach (string legacyId in legacyIds)
{
LegacyEntityHandler? handler = LegacyHandlers.FirstOrDefault(h =>
h.LegacyId == legacyId
);
if (handler is not null)
{
handler.Apply(Entity);
}
else
{
LegacyAliases.Add(id, name);
handler = new(legacyId, Entity);
LegacyHandlers.Add(handler);
}
}
}
public List<EntitySource> GetSources()
{
string[] sourceNames = Input
.GetRequired<JArray>("source")
.Select(token => token.ToString())
.ToArray();
if (sourceNames.Length == 0)
{
throw new(
$"Source for entity with id \"{Entity.Name[LocaleType.English]}\" "
+ $"(Aonprd id \"{Input.GetRequired<string>("id")}\") is not found"
);
}
List<EntitySource> entitySources = new();
foreach (string sourceName in sourceNames)
{
Source source =
Context.Sources.FirstOrDefault(s => s.Name == sourceName)
?? throw new($"Source \"{sourceName}\" did not found");
EntitySource entitySource = new()
{
Entity = Entity,
Source = source,
Suffix = StringHelper.ResolveSource(Input.GetRequired<string>("source_markdown")),
IsRemaster = Input.GetOptional<JArray>("legacy_id") is not null,
};
entitySources.Add(entitySource);
}
return entitySources;
}
public string GenerateExtra(params string[] args)
{
@@ -214,9 +228,7 @@ internal abstract class AbstractEntityProcessor(AonprdProcessorInput input)
return isLowercase ? hash.ToLower() : hash;
}
protected static readonly Dictionary<string, string> LegacyAliases = [];
protected static readonly Dictionary<string, Entity> RemasterWithoutLegacy = [];
protected static readonly List<LegacyEntityHandler> LegacyHandlers = [];
}
public class LegacyItemException(string name)
@@ -9,22 +9,13 @@ internal class ActionProcessor(AonprdProcessorInput input) : AbstractEntityProce
public override Pf2eModel.Model.Structure.Action Object { get; } = new();
private bool _skipping = false;
public override void Add()
{
if (!_skipping)
{
Context.Actions.Add(Object);
}
}
public override void Add() => Context.Actions.Add(Object);
public override void Process()
{
if (Input.GetRequired<string>("markdown").StartsWith("**Activate**"))
{
_skipping = true;
return;
throw new SkipItemException(Input.GetRequired<string>("name"), "activating action");
}
base.Process();
@@ -257,7 +257,7 @@ internal partial class RitualProcessor(AonprdProcessorInput input) : AbstractEnt
}
// Замена символа ссылок
input = StringHelper.MarkdownRegex().Replace(input, "$1[$2]{$3}");
input = StringHelper.LinkRegex().Replace(input, "$1[$2]{$3}");
input = input.Replace("(", "<").Replace(")", ">").Replace("{", "(").Replace("}", ")");
if (input.EndsWith(';') || input.EndsWith(','))
@@ -0,0 +1,115 @@
using System.Data;
using Microsoft.EntityFrameworkCore.Migrations.Operations;
using Newtonsoft.Json.Linq;
using Pf2eModel.Model.Common.I18n;
using Pf2eModel.Model.Enum;
using Pf2eModel.Model.Structure;
using Rule = Pf2eModel.Model.Structure.Rule;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types;
[AonprdProcessor("rules")]
internal class RuleProcessor(AonprdProcessorInput input) : AbstractEntityProcessor(input)
{
public override EntityType Type => EntityType.Rule;
public override Rule Object { get; } = new();
public override void Add() => Context.Rules.Add(Object);
public override void Process()
{
base.Process();
string[] breadcrumbs = (Input.GetOptional<JArray>("breadcrumbs") ?? [])
.Select(token => token.ToString())
.ToArray();
RuleStructureItem item = GetStructureItem(
[.. breadcrumbs, Entity.Name[LocaleType.English]]
);
item.IsHandled = true;
item.RuleId = Object.Id;
item.Id = Input.GetRequired<string>("id");
if (item.Parent?.IsHandled ?? false)
{
Object.ParentId = item.Parent.RuleId!;
// Object.Order = item.Parent.ChildrenOrder?.IndexOf(item.Id) ?? 0;
Object.Order = 0;
}
item.ChildrenOrder = [];
List<int> childrenIds = item
.Children.Select(i => i.RuleId)
.Where(id => id != null)
.OfType<int>()
.ToList();
List<Rule> rules = Context.Rules.Where(i => childrenIds.Contains(i.Id)).ToList();
foreach (Rule rule in rules)
{
rule.Order = 0;
}
/*
* TODO
* Обработка порядка из полей
*
"next_link": {
"label": "Chapter 3: Classes",
"url": "/Rules.aspx?ID=134"
},
"previous_link": {
"label": "Chapter 1: Introduction",
"url": "/Rules.aspx?ID=1"
},
*/
}
private static readonly List<RuleStructureItem> Roots = [];
private RuleStructureItem GetStructureItem(string[] breadcrumbs, RuleStructureItem? from = null)
{
while (true)
{
List<RuleStructureItem> children = from?.Children ?? Roots;
if (breadcrumbs.Length == 0)
{
return from!;
}
string root = breadcrumbs[0];
RuleStructureItem? item = children.FirstOrDefault(i => i.Name == root);
if (item is null)
{
item = new(root, from);
children.Add(item);
}
breadcrumbs = breadcrumbs[1..];
from = item;
}
}
}
internal class RuleStructureItem(string name, RuleStructureItem? parent)
{
public string? Id { get; set; } = null;
public string Name { get; set; } = name;
public int? RuleId { get; set; } = null;
public bool IsHandled { get; set; } = false;
public RuleStructureItem? Parent { get; set; } = parent;
public List<string>? ChildrenOrder { get; set; } = null;
public List<RuleStructureItem> Children { get; } = [];
}
+3
View File
@@ -10,6 +10,9 @@ Pf2EFoundryImporter importer = new();
*/
AonprdImporter importer = new();
importer.ProcessorNames = ["rules"];
importer.Prepare();
importer.Import();
// importer.BuildSchemas();
@@ -13,7 +13,7 @@ using Pf2eModel.Model.Enum;
namespace Pf2eModel.Migrations
{
[DbContext(typeof(Pf2eDbContext))]
[Migration("20250506175505_1")]
[Migration("20250507112626_1")]
partial class _1
{
/// <inheritdoc />
@@ -34,7 +34,7 @@ namespace Pf2eModel.Migrations
NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "deity_fonts", new[] { "none", "heal", "harm", "any" });
NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "deity_sanctification", new[] { "holy", "unholy", "any" });
NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "entity_rarity", new[] { "common", "uncommon", "rare", "unique" });
NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "entity_type", new[] { "unknown", "trait", "action", "ancestry", "archetype", "background", "backpack", "campaign_feature", "class", "subclass_group", "subclass", "condition", "deity", "feat", "heritage", "spell", "ritual", "language" });
NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "entity_type", new[] { "unknown", "trait", "action", "ancestry", "archetype", "background", "backpack", "campaign_feature", "class", "subclass_group", "subclass", "condition", "deity", "feat", "heritage", "spell", "ritual", "language", "rule" });
NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "feat_action_type", new[] { "passive", "action", "reaction", "free" });
NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "feat_category", new[] { "uncategorized", "ancestry-feature", "class-feature", "calling", "class", "skill", "general", "ancestry", "bonus", "deityboon", "curse", "pfsboon", "campaign-feature" });
NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "locale_type", new[] { "english", "legacy_english", "russian", "legacy", "hobby", "other" });
@@ -1395,6 +1395,37 @@ namespace Pf2eModel.Migrations
b.ToTable("ritual_checks", (string)null);
});
modelBuilder.Entity("Pf2eModel.Model.Structure.Rule", b =>
{
b.Property<int>("Id")
.HasColumnType("integer")
.HasColumnName("id")
.HasColumnOrder(1);
b.PrimitiveCollection<string[]>("Breadcrumbs")
.IsRequired()
.HasColumnType("text[]")
.HasColumnName("breadcrumbs");
b.Property<int>("Order")
.HasColumnType("integer")
.HasColumnName("order")
.HasColumnOrder(2);
b.Property<int?>("ParentId")
.HasColumnType("integer")
.HasColumnName("parent_id")
.HasColumnOrder(3);
b.HasKey("Id")
.HasName("pk_rules");
b.HasIndex("ParentId")
.HasDatabaseName("ix_rules_parent_id");
b.ToTable("rules", (string)null);
});
modelBuilder.Entity("Pf2eModel.Model.Structure.Spell", b =>
{
b.Property<int>("Id")
@@ -2249,6 +2280,25 @@ namespace Pf2eModel.Migrations
b.Navigation("Training");
});
modelBuilder.Entity("Pf2eModel.Model.Structure.Rule", b =>
{
b.HasOne("Pf2eModel.Model.Base.Entity", "Entity")
.WithOne()
.HasForeignKey("Pf2eModel.Model.Structure.Rule", "Id")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_rules_entities_id");
b.HasOne("Pf2eModel.Model.Structure.Rule", "Parent")
.WithMany("Children")
.HasForeignKey("ParentId")
.HasConstraintName("fk_rules_rules_parent_id");
b.Navigation("Entity");
b.Navigation("Parent");
});
modelBuilder.Entity("Pf2eModel.Model.Structure.Spell", b =>
{
b.HasOne("Pf2eModel.Model.Base.Entity", "Entity")
@@ -2426,6 +2476,11 @@ namespace Pf2eModel.Migrations
b.Navigation("Checks");
});
modelBuilder.Entity("Pf2eModel.Model.Structure.Rule", b =>
{
b.Navigation("Children");
});
modelBuilder.Entity("Pf2eModel.Model.Structure.Spell", b =>
{
b.Navigation("SpellAreas");
@@ -24,7 +24,7 @@ namespace Pf2eModel.Migrations
.Annotation("Npgsql:Enum:deity_fonts", "none,heal,harm,any")
.Annotation("Npgsql:Enum:deity_sanctification", "holy,unholy,any")
.Annotation("Npgsql:Enum:entity_rarity", "common,uncommon,rare,unique")
.Annotation("Npgsql:Enum:entity_type", "unknown,trait,action,ancestry,archetype,background,backpack,campaign_feature,class,subclass_group,subclass,condition,deity,feat,heritage,spell,ritual,language")
.Annotation("Npgsql:Enum:entity_type", "unknown,trait,action,ancestry,archetype,background,backpack,campaign_feature,class,subclass_group,subclass,condition,deity,feat,heritage,spell,ritual,language,rule")
.Annotation("Npgsql:Enum:feat_action_type", "passive,action,reaction,free")
.Annotation("Npgsql:Enum:feat_category", "uncategorized,ancestry-feature,class-feature,calling,class,skill,general,ancestry,bonus,deityboon,curse,pfsboon,campaign-feature")
.Annotation("Npgsql:Enum:locale_type", "english,legacy_english,russian,legacy,hobby,other")
@@ -324,6 +324,31 @@ namespace Pf2eModel.Migrations
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "rules",
columns: table => new
{
id = table.Column<int>(type: "integer", nullable: false),
order = table.Column<int>(type: "integer", nullable: false),
parent_id = table.Column<int>(type: "integer", nullable: true),
breadcrumbs = table.Column<string[]>(type: "text[]", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("pk_rules", x => x.id);
table.ForeignKey(
name: "fk_rules_entities_id",
column: x => x.id,
principalTable: "entities",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_rules_rules_parent_id",
column: x => x.parent_id,
principalTable: "rules",
principalColumn: "id");
});
migrationBuilder.CreateTable(
name: "spells",
columns: table => new
@@ -1275,6 +1300,11 @@ namespace Pf2eModel.Migrations
table: "ritual_checks",
column: "training_id");
migrationBuilder.CreateIndex(
name: "ix_rules_parent_id",
table: "rules",
column: "parent_id");
migrationBuilder.CreateIndex(
name: "ix_spell_areas_spell_id",
table: "spell_areas",
@@ -1400,6 +1430,9 @@ namespace Pf2eModel.Migrations
migrationBuilder.DropTable(
name: "ritual_checks");
migrationBuilder.DropTable(
name: "rules");
migrationBuilder.DropTable(
name: "spell_areas");
@@ -31,7 +31,7 @@ namespace Pf2eModel.Migrations
NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "deity_fonts", new[] { "none", "heal", "harm", "any" });
NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "deity_sanctification", new[] { "holy", "unholy", "any" });
NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "entity_rarity", new[] { "common", "uncommon", "rare", "unique" });
NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "entity_type", new[] { "unknown", "trait", "action", "ancestry", "archetype", "background", "backpack", "campaign_feature", "class", "subclass_group", "subclass", "condition", "deity", "feat", "heritage", "spell", "ritual", "language" });
NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "entity_type", new[] { "unknown", "trait", "action", "ancestry", "archetype", "background", "backpack", "campaign_feature", "class", "subclass_group", "subclass", "condition", "deity", "feat", "heritage", "spell", "ritual", "language", "rule" });
NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "feat_action_type", new[] { "passive", "action", "reaction", "free" });
NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "feat_category", new[] { "uncategorized", "ancestry-feature", "class-feature", "calling", "class", "skill", "general", "ancestry", "bonus", "deityboon", "curse", "pfsboon", "campaign-feature" });
NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "locale_type", new[] { "english", "legacy_english", "russian", "legacy", "hobby", "other" });
@@ -1392,6 +1392,37 @@ namespace Pf2eModel.Migrations
b.ToTable("ritual_checks", (string)null);
});
modelBuilder.Entity("Pf2eModel.Model.Structure.Rule", b =>
{
b.Property<int>("Id")
.HasColumnType("integer")
.HasColumnName("id")
.HasColumnOrder(1);
b.PrimitiveCollection<string[]>("Breadcrumbs")
.IsRequired()
.HasColumnType("text[]")
.HasColumnName("breadcrumbs");
b.Property<int>("Order")
.HasColumnType("integer")
.HasColumnName("order")
.HasColumnOrder(2);
b.Property<int?>("ParentId")
.HasColumnType("integer")
.HasColumnName("parent_id")
.HasColumnOrder(3);
b.HasKey("Id")
.HasName("pk_rules");
b.HasIndex("ParentId")
.HasDatabaseName("ix_rules_parent_id");
b.ToTable("rules", (string)null);
});
modelBuilder.Entity("Pf2eModel.Model.Structure.Spell", b =>
{
b.Property<int>("Id")
@@ -2246,6 +2277,25 @@ namespace Pf2eModel.Migrations
b.Navigation("Training");
});
modelBuilder.Entity("Pf2eModel.Model.Structure.Rule", b =>
{
b.HasOne("Pf2eModel.Model.Base.Entity", "Entity")
.WithOne()
.HasForeignKey("Pf2eModel.Model.Structure.Rule", "Id")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_rules_entities_id");
b.HasOne("Pf2eModel.Model.Structure.Rule", "Parent")
.WithMany("Children")
.HasForeignKey("ParentId")
.HasConstraintName("fk_rules_rules_parent_id");
b.Navigation("Entity");
b.Navigation("Parent");
});
modelBuilder.Entity("Pf2eModel.Model.Structure.Spell", b =>
{
b.HasOne("Pf2eModel.Model.Base.Entity", "Entity")
@@ -2423,6 +2473,11 @@ namespace Pf2eModel.Migrations
b.Navigation("Checks");
});
modelBuilder.Entity("Pf2eModel.Model.Structure.Rule", b =>
{
b.Navigation("Children");
});
modelBuilder.Entity("Pf2eModel.Model.Structure.Spell", b =>
{
b.Navigation("SpellAreas");
+3
View File
@@ -57,4 +57,7 @@ public enum EntityType
[PgName("language")]
Language,
[PgName("rule")]
Rule,
}
+29
View File
@@ -0,0 +1,29 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Pf2eModel.Model.Base;
using Pf2eModel.Model.Enum;
namespace Pf2eModel.Model.Structure;
[EntityOfType(EntityType.Rule)]
public class Rule : IEntity
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[Column(Order = 1)]
public int Id { get; set; }
public Entity Entity { get; set; } = null!;
[Column(Order = 2)]
public int Order { get; set; }
[Column(Order = 3)]
public int? ParentId { get; set; } = null;
public Rule? Parent { get; set; } = null;
public ICollection<Rule> Children { get; set; } = [];
public string[] Breadcrumbs { get; set; } = [];
}
+2
View File
@@ -100,6 +100,8 @@ public partial class Pf2eDbContext : DbContext
public virtual DbSet<RitualCheck> RitualChecks { get; set; }
public virtual DbSet<Rule> Rules { get; set; }
public virtual DbSet<Source> Sources { get; set; }
public virtual DbSet<Spell> Spells { get; set; }
+12
View File
@@ -503,6 +503,18 @@ public partial class Pf2eDbContext
{
BindEntity(entity);
});
// Rule
modelBuilder.Entity<Rule>(entity =>
{
BindEntity(entity);
entity
.HasOne(r => r.Parent)
.WithMany(r => r.Children)
.HasForeignKey(r => r.ParentId)
.IsRequired(false);
});
}
private void DefineMethods(ModelBuilder modelBuilder)