From 995029ce8d01ab216954cba9b79dc13b21def035 Mon Sep 17 00:00:00 2001 From: kozhilya Date: Wed, 14 May 2025 15:44:32 +0400 Subject: [PATCH] =?UTF-8?q?=D0=9F=D0=B5=D1=80=D0=B5=D0=BE=D1=81=D0=BC?= =?UTF-8?q?=D1=8B=D1=81=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=B0=D1=82=D1=82?= =?UTF-8?q?=D1=80=D0=B8=D0=B1=D1=83=D1=82=D0=BE=D0=B2=20=D0=BF=D0=B5=D1=80?= =?UTF-8?q?=D0=B5=D0=B1=D0=B5=D1=80=D0=B0=D1=8E=D1=89=D0=B8=D1=85=20=D0=BF?= =?UTF-8?q?=D1=80=D0=BE=D1=86=D0=B5=D1=81=D1=81=D0=BE=D1=80=D0=BE=D0=B2=20?= =?UTF-8?q?=D0=B8=20=D1=83=D1=81=D1=82=D0=B0=D1=80=D0=B5=D0=B2=D1=88=D1=8B?= =?UTF-8?q?=D1=85=20=D1=8D=D0=BB=D0=B5=D0=BC=D0=B5=D0=BD=D1=82=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Pf2eDataScrapper/App/ProcessorAttribute.cs | 39 +++- Pf2eDataScrapper/App/ProcessorCollection.cs | 29 ++- Pf2eDataScrapper/App/ProcessorGroupEnum.cs | 6 + .../Processors/AbstractEntityProcessor.cs | 23 +- .../Processors/Common/LegacyProcessor.cs | 125 ++++++++++ .../Processors/Common/SkillProcessor.cs | 3 +- .../Processors/Common/SourceProcessor.cs | 3 +- .../Subclasses/ApparitionProcessor.cs | 3 +- .../Processors/Types/ActionProcessor.cs | 3 +- .../Processors/Types/AncestryProcessor.cs | 3 +- .../Processors/Types/ArchetypeProcessor.cs | 3 +- .../Processors/Types/BackgroundProcessor.cs | 3 +- .../Aonprd/Processors/Types/ClassProcessor.cs | 3 +- .../Aonprd/Processors/Types/FeatProcessor.cs | 3 +- .../Processors/Types/LanguageProcessor.cs | 3 +- .../Processors/Types/RitualProcessor.cs | 3 +- .../Processors/Types/Rules/RuleProcessor.cs | 6 +- .../Aonprd/Processors/Types/SpellProcessor.cs | 3 +- .../Aonprd/Processors/Types/TraitProcessor.cs | 5 +- .../Processors/Entities/ActionProcessor.cs | 3 +- .../Processors/Entities/ConditionProcessor.cs | 3 +- .../Processors/FoundryEnAbstractProcessor.cs | 1 + .../Processors/ProcessorExecutor.cs | 26 ++- Pf2eDataScrapper/Processors/ProcessorInput.cs | 16 +- ...signer.cs => 20250514102154_1.Designer.cs} | 214 +++++++++++++++++- ...0250514084949_1.cs => 20250514102154_1.cs} | 137 ++++++++++- .../Migrations/Pf2eDbContextModelSnapshot.cs | 212 +++++++++++++++++ Pf2eModel/Model/Base/Entity.cs | 6 + Pf2eModel/Model/Base/Legacy/LegacyEntity.cs | 42 ++++ .../Model/Base/Legacy/LegacyEntityExternal.cs | 58 +++++ .../Model/Base/Legacy/LegacyEntitySource.cs | 59 +++++ Pf2eModel/Model/Base/Source/Source.cs | 6 + .../Model/Base/Targets/EntityExternal.cs | 4 +- .../Model/Base/Targets/ExternalTarget.cs | 6 + Pf2eModel/Pf2eDbContext.cs | 7 + Pf2eModel/Pf2eDbContextModel.cs | 35 +++ 36 files changed, 1036 insertions(+), 68 deletions(-) create mode 100644 Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Common/LegacyProcessor.cs rename Pf2eModel/Migrations/{20250514084949_1.Designer.cs => 20250514102154_1.Designer.cs} (92%) rename Pf2eModel/Migrations/{20250514084949_1.cs => 20250514102154_1.cs} (91%) create mode 100644 Pf2eModel/Model/Base/Legacy/LegacyEntity.cs create mode 100644 Pf2eModel/Model/Base/Legacy/LegacyEntityExternal.cs create mode 100644 Pf2eModel/Model/Base/Legacy/LegacyEntitySource.cs diff --git a/Pf2eDataScrapper/App/ProcessorAttribute.cs b/Pf2eDataScrapper/App/ProcessorAttribute.cs index 0f3a04f..aa02c8d 100644 --- a/Pf2eDataScrapper/App/ProcessorAttribute.cs +++ b/Pf2eDataScrapper/App/ProcessorAttribute.cs @@ -22,15 +22,44 @@ public class ProcessorAttribute(string name) : Attribute /// public ProcessorGroupEnum Group { get; init; } = ProcessorGroupEnum.General; - /// - /// Коллекция названий обработчиков, которые должны быть выполнены до этого оброботчика. - /// - public string? InnerType { get; init; } = null; - /// /// Процессор включён по умолчанию. /// public bool Active { get; init; } = true; + + /// + /// Разрешить выполнять процессов в параллельном режиме + /// + public bool Parallel { get; init; } = true; + + /// + /// Обработчик набора элементов + /// + public virtual bool Collection { get; } = false; +} + +/// +/// Аттрибут обработчика набор элементов. +/// +/// Название обработчика. +[AttributeUsage(AttributeTargets.Class)] +public class ProcessorCollectionAttribute(string name) : ProcessorAttribute(name) +{ + /// + /// Название типов, который обрабатывает этот оброботчик. + /// null - обработать все типы. + /// + public required string? InnerType { get; init; } + + /// + /// Тип используемого компендиума. + /// + public required ProcessorCompendiumEnum CompendiumType { get; init; } + + /// + /// Обработчик набора элементов. + /// + public override bool Collection { get; } = true; } /// diff --git a/Pf2eDataScrapper/App/ProcessorCollection.cs b/Pf2eDataScrapper/App/ProcessorCollection.cs index e2b7edf..bd7b61c 100644 --- a/Pf2eDataScrapper/App/ProcessorCollection.cs +++ b/Pf2eDataScrapper/App/ProcessorCollection.cs @@ -24,7 +24,7 @@ internal class ProcessorCollection /// /// /// - public ProcessorCollectionItem this[string name] => Items.First(item => item.Name == name); + public ProcessorCollectionItem this[string name] => AllItems.First(item => item.Name == name); /// /// Конструктор класса . @@ -65,7 +65,19 @@ internal class ProcessorCollection ProcessorDependenciesAttribute? dependenciesAttribute = type.GetCustomAttribute(); - bool isCollection = processorAttribute.InnerType is not null; + bool isCollection = processorAttribute.Collection; + + string? innerType = null; + ProcessorCompendiumEnum? compendium = null; + + if (processorAttribute.GetType() == typeof(ProcessorCollectionAttribute)) + { + ProcessorCollectionAttribute processorCollectionAttribute = + (ProcessorCollectionAttribute)processorAttribute; + + innerType = processorCollectionAttribute.InnerType; + compendium = processorCollectionAttribute.CompendiumType; + } return new() { @@ -74,8 +86,9 @@ internal class ProcessorCollection Description = processorAttribute.Description, Group = processorAttribute.Group, Dependencies = dependenciesAttribute?.Dependencies ?? [], - IsCollectionProcessor = isCollection, - InnerType = processorAttribute.InnerType, + IsCollection = isCollection, + InnerType = innerType, + Compendium = compendium, Active = processorAttribute.Active, }; } @@ -216,10 +229,16 @@ internal class ProcessorCollectionItem /// Флаг, указывающий, обрабатывает ли обработчик коллекцию элементов. /// Внутренний тип элементов указывается в . /// - public required bool IsCollectionProcessor { get; init; } + public required bool IsCollection { get; init; } /// /// Флаг, указывающий внутренний тип коллекции обрабатываемых элементов. /// public string? InnerType { get; init; } + + /// + /// Название типов, который обрабатывает этот оброботчик. + /// null - обработать все типы. + /// + public ProcessorCompendiumEnum? Compendium { get; init; } } diff --git a/Pf2eDataScrapper/App/ProcessorGroupEnum.cs b/Pf2eDataScrapper/App/ProcessorGroupEnum.cs index a0c36e5..fcf7fc6 100644 --- a/Pf2eDataScrapper/App/ProcessorGroupEnum.cs +++ b/Pf2eDataScrapper/App/ProcessorGroupEnum.cs @@ -24,6 +24,12 @@ public enum ProcessorGroupEnum FoundryEnEntities, } +public enum ProcessorCompendiumEnum +{ + Aonprd, + FoundryEn, +} + public static class ProcessorGroupEnumExtension { public static string GetDescription(this ProcessorGroupEnum group) diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/AbstractEntityProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/AbstractEntityProcessor.cs index 5bfbe31..19f47c3 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/AbstractEntityProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/AbstractEntityProcessor.cs @@ -13,6 +13,7 @@ using Pf2eDataScrapper.Processors; using Pf2eModel; using Pf2eModel.Model; using Pf2eModel.Model.Base; +using Pf2eModel.Model.Base.Legacy; using Pf2eModel.Model.Base.Source; using Pf2eModel.Model.Base.Targets; using Pf2eModel.Model.Common.I18n; @@ -160,8 +161,9 @@ internal abstract class AbstractEntityProcessor( { Entity = Entity, Target = Globals.AonprdTarget, + ExternalId = Input.GetRequired("id"), Suffix = Input.GetRequired("url"), - Md5 = Md5(Input.Input.ToString()), + Md5 = Input.Md5(), }; Context.EntityExternals.Add(external); @@ -223,11 +225,6 @@ internal abstract class AbstractEntityProcessor( return; } - if (legacyIdArray.Count > 1) - { - throw new("Несколько отметок ремастер-сущности на устаревшие!"); - } - string[] legacyIds = legacyIdArray.ToArray().Select(t => t.ToString()).ToArray(); if (legacyIds.Length == 0) @@ -345,19 +342,5 @@ internal abstract class AbstractEntityProcessor( 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 ConcurrentBag LegacyHandlers = []; } diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Common/LegacyProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Common/LegacyProcessor.cs new file mode 100644 index 0000000..f0c311d --- /dev/null +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Common/LegacyProcessor.cs @@ -0,0 +1,125 @@ +using Newtonsoft.Json.Linq; +using Pf2eDataScrapper.App; +using Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Helpers; +using Pf2eDataScrapper.Importers.Implementations.Aonprd.Helpers; +using Pf2eDataScrapper.Processors; +using Pf2eModel; +using Pf2eModel.Model.Base.Legacy; +using Pf2eModel.Model.Base.Source; +using Pf2eModel.Model.Common.I18n; + +namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.Processors.Common; + +[ProcessorCollection( + "aonprd:legacy", + InnerType = null, + CompendiumType = ProcessorCompendiumEnum.Aonprd, + Description = "Создание ссылок на устаревшие сущности", + Group = ProcessorGroupEnum.AonprdPreparation +)] +[ProcessorDependencies(["aonprd:source", "aonprd:external"])] +internal class LegacyProcessor( + ProcessorInput input, + Pf2eDbContext context, + AonprdBuilderGlobals globals, + StringHelper stringHelper +) : AbstractInputProcessor(input) +{ + public Pf2eDbContext Context { get; } = context; + public AonprdBuilderGlobals Globals { get; } = globals; + public StringHelper StringHelper { get; } = stringHelper; + + private LegacyEntity LegacyEntity { get; set; } = null!; + + public override void Process() + { + JArray? remasterIdArray = Input.GetOptional("remaster_id"); + + if (Input.GetOptional("remaster_id") is null) + { + throw new SilentSkipItemException(); + } + + LegacyEntity = new() + { + Name = { [LocaleType.English] = Input.GetOptional("name", "sidebar-29") }, + }; + + AddSource(); + AddExternal(); + } + + /// + /// Генерация источников сущности. + /// + protected virtual void AddSource() + { + foreach (LegacyEntitySource entitySource in GetSources()) + { + LegacyEntity.Sources.Add(entitySource); + } + } + + /// + /// Получение списка источников сущности. + /// + /// + public List GetSources() + { + string[] sourceNames = Input + .GetRequired("source") + .Select(token => token.ToString()) + .ToArray(); + + if (sourceNames.Length == 0) + { + throw new( + $"Source for entity with id \"{LegacyEntity.Name[LocaleType.English]}\" " + + $"(Aonprd id \"{Input.GetRequired("id")}\") is not found" + ); + } + + List entitySources = []; + + foreach (string sourceName in sourceNames) + { + Source source = + Context.Sources.FirstOrDefault(s => s.Name == sourceName) + ?? throw new($"Source \"{sourceName}\" did not found"); + + LegacyEntitySource entitySource = new() + { + LegacyEntity = LegacyEntity, + Source = source, + Suffix = StringHelper.ResolveSource(Input.GetRequired("source_markdown")), + IsRemaster = Input.GetOptional("legacy_id") is not null, + }; + + entitySources.Add(entitySource); + } + + return entitySources; + } + + /// + /// Генерация внешних ссылок сущности. + /// + protected virtual void AddExternal() + { + LegacyEntityExternal external = new() + { + LegacyEntity = LegacyEntity, + Target = Globals.AonprdTarget, + ExternalId = Input.GetRequired("id"), + Suffix = Input.GetRequired("url"), + Md5 = Input.Md5(), + }; + + Context.LegacyEntityExternals.Add(external); + } + + public override void Save() + { + Context.LegacyEntities.Add(LegacyEntity); + } +} diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Common/SkillProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Common/SkillProcessor.cs index 9c84231..03c9666 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Common/SkillProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Common/SkillProcessor.cs @@ -7,8 +7,9 @@ using Pf2eModel.Model.Enum; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.Processors.Common; -[Processor( +[ProcessorCollection( "aonprd:skill", + CompendiumType = ProcessorCompendiumEnum.Aonprd, InnerType = "skill", Description = "Навыки", Group = ProcessorGroupEnum.AonprdPreparation diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Common/SourceProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Common/SourceProcessor.cs index c20c2c0..570cf42 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Common/SourceProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Common/SourceProcessor.cs @@ -6,9 +6,10 @@ using Pf2eModel.Model.Enum; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.Processors.Common; -[Processor( +[ProcessorCollection( "aonprd:source", InnerType = "source", + CompendiumType = ProcessorCompendiumEnum.Aonprd, Description = "Источники", Group = ProcessorGroupEnum.AonprdPreparation )] diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Subclasses/ApparitionProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Subclasses/ApparitionProcessor.cs index ad7985a..47d2cc9 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Subclasses/ApparitionProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Subclasses/ApparitionProcessor.cs @@ -12,9 +12,10 @@ using Pf2eModel.Model.Structure; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.Processors.Subclasses; -[Processor( +[ProcessorCollection( "aonprd:apparition", InnerType = "apparition", + CompendiumType = ProcessorCompendiumEnum.Aonprd, Description = "Apparitions", Group = ProcessorGroupEnum.AonprdSubclasses )] diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/ActionProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/ActionProcessor.cs index 56174e3..2cbc2fc 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/ActionProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/ActionProcessor.cs @@ -8,9 +8,10 @@ using Pf2eModel.Model.Enum; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.Processors.Types; -[Processor( +[ProcessorCollection( "aonprd:action", InnerType = "action", + CompendiumType = ProcessorCompendiumEnum.Aonprd, Description = "Действия", Group = ProcessorGroupEnum.AonprdEntities )] diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/AncestryProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/AncestryProcessor.cs index a3aa582..3631619 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/AncestryProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/AncestryProcessor.cs @@ -10,9 +10,10 @@ using Pf2eModel.Model.Structure; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.Processors.Types; -[Processor( +[ProcessorCollection( "aonprd:ancestry", InnerType = "ancestry", + CompendiumType = ProcessorCompendiumEnum.Aonprd, Description = "Родословные", Group = ProcessorGroupEnum.AonprdEntities )] diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/ArchetypeProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/ArchetypeProcessor.cs index a97727a..e8d9b62 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/ArchetypeProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/ArchetypeProcessor.cs @@ -9,9 +9,10 @@ using Pf2eModel.Model.Structure; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.Processors.Types; -[Processor( +[ProcessorCollection( "aonprd:archetype", InnerType = "archetype", + CompendiumType = ProcessorCompendiumEnum.Aonprd, Description = "Архетипы", Group = ProcessorGroupEnum.AonprdEntities )] diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/BackgroundProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/BackgroundProcessor.cs index 1b9b7cf..2d857d6 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/BackgroundProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/BackgroundProcessor.cs @@ -11,9 +11,10 @@ using Pf2eModel.Model.Structure.BackgroundRelation; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.Processors.Types; -[Processor( +[ProcessorCollection( "aonprd:background", InnerType = "background", + CompendiumType = ProcessorCompendiumEnum.Aonprd, Description = "Предыстории", Group = ProcessorGroupEnum.AonprdEntities )] diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/ClassProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/ClassProcessor.cs index 2d3a05f..869bcd2 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/ClassProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/ClassProcessor.cs @@ -15,9 +15,10 @@ using static System.Text.RegularExpressions.Regex; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.Processors.Types; -[Processor( +[ProcessorCollection( "aonprd:class", InnerType = "class", + CompendiumType = ProcessorCompendiumEnum.Aonprd, Description = "Классы", Group = ProcessorGroupEnum.AonprdEntities )] diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/FeatProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/FeatProcessor.cs index 05a517f..e1e01da 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/FeatProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/FeatProcessor.cs @@ -10,9 +10,10 @@ using Pf2eModel.Model.Structure; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.Processors.Types; -[Processor( +[ProcessorCollection( "aonprd:feat", InnerType = "feat", + CompendiumType = ProcessorCompendiumEnum.Aonprd, Description = "Черты", Group = ProcessorGroupEnum.AonprdEntities )] diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/LanguageProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/LanguageProcessor.cs index 7d2960a..9ded0f6 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/LanguageProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/LanguageProcessor.cs @@ -9,9 +9,10 @@ using Pf2eModel.Model.Structure; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.Processors.Types; -[Processor( +[ProcessorCollection( "aonprd:language", InnerType = "language", + CompendiumType = ProcessorCompendiumEnum.Aonprd, Description = "Языки", Group = ProcessorGroupEnum.AonprdEntities )] diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/RitualProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/RitualProcessor.cs index 5d96c0e..1a73ccd 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/RitualProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/RitualProcessor.cs @@ -13,9 +13,10 @@ using Pf2eModel.Model.Structure.RitualRelation; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.Processors.Types; -[Processor( +[ProcessorCollection( "aonprd:ritual", InnerType = "ritual", + CompendiumType = ProcessorCompendiumEnum.Aonprd, Description = "Ритуалы", Group = ProcessorGroupEnum.AonprdEntities )] diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/Rules/RuleProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/Rules/RuleProcessor.cs index 5d6737e..6534154 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/Rules/RuleProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/Rules/RuleProcessor.cs @@ -5,6 +5,7 @@ using Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Helpers; using Pf2eDataScrapper.Importers.Implementations.Aonprd.Helpers; using Pf2eDataScrapper.Processors; using Pf2eModel; +using Pf2eModel.Model.Base.Legacy; using Pf2eModel.Model.Base.Source; using Pf2eModel.Model.Base.Targets; using Pf2eModel.Model.Common.I18n; @@ -13,10 +14,11 @@ using Rule = Pf2eModel.Model.Structure.Rule; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.Processors.Types.Rules; -[Processor( +[ProcessorCollection( "aonprd:rules", InnerType = "rules", - Description = "Правила", + CompendiumType = ProcessorCompendiumEnum.Aonprd, + Description = "Создание цели внешних ссылок", Group = ProcessorGroupEnum.AonprdEntities )] [ProcessorDependencies(["aonprd:trait", "aonprd:source", "aonprd:rules-toc"])] diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/SpellProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/SpellProcessor.cs index d69b536..9a4aaf9 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/SpellProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/SpellProcessor.cs @@ -12,9 +12,10 @@ using Pf2eModel.Model.Structure.SpellRelation; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.Processors.Types; -[Processor( +[ProcessorCollection( "aonprd:spell", InnerType = "spell", + CompendiumType = ProcessorCompendiumEnum.Aonprd, Description = "Заклинания", Group = ProcessorGroupEnum.AonprdEntities )] diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/TraitProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/TraitProcessor.cs index 39a03c1..80e55db 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/TraitProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/TraitProcessor.cs @@ -9,13 +9,14 @@ using Pf2eModel.Model.Structure; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.Processors.Types; -[Processor( +[ProcessorCollection( "aonprd:trait", InnerType = "trait", + CompendiumType = ProcessorCompendiumEnum.Aonprd, Description = "Признаки", Group = ProcessorGroupEnum.AonprdEntities )] -[ProcessorDependencies(["aonprd:external", "aonprd:source"])] +[ProcessorDependencies(["aonprd:legacy"])] internal class TraitProcessor( ProcessorInput input, Pf2eDbContext context, diff --git a/Pf2eDataScrapper/Importers/Implementations/FoundryEn/Processors/Entities/ActionProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/FoundryEn/Processors/Entities/ActionProcessor.cs index b07acf0..93777b2 100644 --- a/Pf2eDataScrapper/Importers/Implementations/FoundryEn/Processors/Entities/ActionProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/FoundryEn/Processors/Entities/ActionProcessor.cs @@ -7,9 +7,10 @@ using Pf2eModel.Model.Enum; namespace Pf2eDataScrapper.Importers.Implementations.FoundryEn.Processors.Entities; -[Processor( +[ProcessorCollection( "foundry-en:action", InnerType = "action", + CompendiumType = ProcessorCompendiumEnum.FoundryEn, Description = "Действия", Group = ProcessorGroupEnum.FoundryEnEntities, Active = false diff --git a/Pf2eDataScrapper/Importers/Implementations/FoundryEn/Processors/Entities/ConditionProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/FoundryEn/Processors/Entities/ConditionProcessor.cs index c967d8d..1c34cbd 100644 --- a/Pf2eDataScrapper/Importers/Implementations/FoundryEn/Processors/Entities/ConditionProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/FoundryEn/Processors/Entities/ConditionProcessor.cs @@ -8,9 +8,10 @@ using Pf2eModel.Model.Structure; namespace Pf2eDataScrapper.Importers.Implementations.FoundryEn.Processors.Entities; -[Processor( +[ProcessorCollection( "foundry-en:condition", InnerType = "Item.condition", + CompendiumType = ProcessorCompendiumEnum.FoundryEn, Description = "Состояния", Group = ProcessorGroupEnum.FoundryEnEntities, Active = false diff --git a/Pf2eDataScrapper/Importers/Implementations/FoundryEn/Processors/FoundryEnAbstractProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/FoundryEn/Processors/FoundryEnAbstractProcessor.cs index 829fc9d..5874ed4 100644 --- a/Pf2eDataScrapper/Importers/Implementations/FoundryEn/Processors/FoundryEnAbstractProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/FoundryEn/Processors/FoundryEnAbstractProcessor.cs @@ -7,6 +7,7 @@ using Pf2eDataScrapper.Processors; using Pf2eModel; using Pf2eModel.Model; using Pf2eModel.Model.Base; +using Pf2eModel.Model.Base.Legacy; using Pf2eModel.Model.Base.Source; using Pf2eModel.Model.Base.Targets; using Pf2eModel.Model.Common.I18n; diff --git a/Pf2eDataScrapper/Processors/ProcessorExecutor.cs b/Pf2eDataScrapper/Processors/ProcessorExecutor.cs index 831d350..9d74fa0 100644 --- a/Pf2eDataScrapper/Processors/ProcessorExecutor.cs +++ b/Pf2eDataScrapper/Processors/ProcessorExecutor.cs @@ -59,6 +59,10 @@ internal sealed class ProcessorExecutor( { Progress.Information(exception.Message); } + catch (SilentSkipItemException) + { + // + } } Context.SaveChanges(); }; @@ -85,7 +89,7 @@ internal sealed class ProcessorExecutor( private IEnumerable IterateProcessors(ProcessorCollectionItem item) { - if (!item.IsCollectionProcessor) + if (!item.IsCollection) { Progress.Local.Reset(1); Progress.Local.Start(item.Description); @@ -101,21 +105,20 @@ internal sealed class ProcessorExecutor( ); } - AbstractCompendium compendium = item.Group switch + AbstractCompendium compendium = item.Compendium switch { - ProcessorGroupEnum.AonprdPreparation - or ProcessorGroupEnum.AonprdEntities - or ProcessorGroupEnum.AonprdSubclasses => AonprdCompendium, - ProcessorGroupEnum.FoundryEnPreparation or ProcessorGroupEnum.FoundryEnEntities => - FoundryEnCompendium, + ProcessorCompendiumEnum.Aonprd => AonprdCompendium, + ProcessorCompendiumEnum.FoundryEn => FoundryEnCompendium, _ => throw new($"Не могу выбрать импортёр для группы {item.Group}"), }; - Progress.Local.Reset(compendium.Count(item.InnerType!)); + Progress.Local.Reset( + item.InnerType is null ? compendium.Count() : compendium.Count(item.InnerType) + ); foreach ( (AbstractCompendiumItem compendiumItem, JObject json) in compendium.Iterate( - item.InnerType! + item.InnerType ) ) { @@ -150,3 +153,8 @@ public class LegacyItemException(string name) : SkipItemException(name, "Уст /// Причина пропуска. public class SkipItemException(string name, string reason) : Exception($"Пропускаю элемент '{name}', указана причина: {reason}.") { } + +/// +/// Исключение, показывающее, что обработка сущности пропускается без вывода сообщения. +/// +public class SilentSkipItemException : Exception { } diff --git a/Pf2eDataScrapper/Processors/ProcessorInput.cs b/Pf2eDataScrapper/Processors/ProcessorInput.cs index d1351da..e0663b1 100644 --- a/Pf2eDataScrapper/Processors/ProcessorInput.cs +++ b/Pf2eDataScrapper/Processors/ProcessorInput.cs @@ -1,4 +1,6 @@ -using Newtonsoft.Json.Linq; +using System.Security.Cryptography; +using System.Text; +using Newtonsoft.Json.Linq; using NpgsqlTypes; using Pf2eModel.Model.Common.I18n; @@ -181,6 +183,18 @@ internal class ProcessorInput(string name, JObject input, IEnumerable + /// Создание контрольной суммы объекта. + /// + /// Использовать шестнадцатеричные цифры в нижнем регистре. + /// Контрольная сумма объекта. + public string Md5(bool isLowercase = false) + { + byte[] byteHash = MD5.HashData(Encoding.UTF8.GetBytes(Input.ToString())); + string hash = Convert.ToHexString(byteHash).Replace("-", ""); + return isLowercase ? hash.ToLower() : hash; + } + /// /// Исключение при неверном значении для перечисленния. /// diff --git a/Pf2eModel/Migrations/20250514084949_1.Designer.cs b/Pf2eModel/Migrations/20250514102154_1.Designer.cs similarity index 92% rename from Pf2eModel/Migrations/20250514084949_1.Designer.cs rename to Pf2eModel/Migrations/20250514102154_1.Designer.cs index 66ebb3f..fc193fc 100644 --- a/Pf2eModel/Migrations/20250514084949_1.Designer.cs +++ b/Pf2eModel/Migrations/20250514102154_1.Designer.cs @@ -13,7 +13,7 @@ using Pf2eModel.Model.Enum; namespace Pf2eModel.Migrations { [DbContext(typeof(Pf2eDbContext))] - [Migration("20250514084949_1")] + [Migration("20250514102154_1")] partial class _1 { /// @@ -86,6 +86,25 @@ namespace Pf2eModel.Migrations b.ToTable("entity_keyword", (string)null); }); + modelBuilder.Entity("EntityLegacyEntity", b => + { + b.Property("LegacyEntitiesId") + .HasColumnType("integer") + .HasColumnName("legacy_entities_id"); + + b.Property("RemasterEntitiesId") + .HasColumnType("integer") + .HasColumnName("remaster_entities_id"); + + b.HasKey("LegacyEntitiesId", "RemasterEntitiesId") + .HasName("pk_entity_legacy_entity"); + + b.HasIndex("RemasterEntitiesId") + .HasDatabaseName("ix_entity_legacy_entity_remaster_entities_id"); + + b.ToTable("entity_legacy_entity", (string)null); + }); + modelBuilder.Entity("EntityTrait", b => { b.Property("EntitiesId") @@ -240,6 +259,123 @@ namespace Pf2eModel.Migrations b.ToTable("keyword_aliases", (string)null); }); + modelBuilder.Entity("Pf2eModel.Model.Base.Legacy.LegacyEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id") + .HasColumnOrder(1); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasColumnType("jsonb") + .HasColumnName("name") + .HasColumnOrder(2) + .HasComment("Отображаемое имя"); + + b.HasKey("Id") + .HasName("pk_legacy_entities"); + + b.ToTable("legacy_entities", (string)null); + }); + + modelBuilder.Entity("Pf2eModel.Model.Base.Legacy.LegacyEntityExternal", b => + { + b.Property("LegacyEntityId") + .HasColumnType("integer") + .HasColumnName("legacy_entity_id") + .HasColumnOrder(1) + .HasComment("Идентификатор сущности"); + + b.Property("TargetId") + .HasColumnType("integer") + .HasColumnName("target_id") + .HasColumnOrder(2) + .HasComment("Идентификатор цели"); + + b.Property("ExternalId") + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("external_id") + .HasColumnOrder(3); + + b.Property("Md5") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("character varying(32)") + .HasColumnName("md5") + .HasColumnOrder(5) + .HasComment("Контрольная сумма объекта"); + + b.Property("Suffix") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("suffix") + .HasColumnOrder(4) + .HasComment("Часть URL до объекта. Для источников Foundry - полный UUID объекта"); + + b.HasKey("LegacyEntityId", "TargetId") + .HasName("pk_legacy_entity_externals"); + + b.HasIndex("TargetId") + .HasDatabaseName("ix_legacy_entity_externals_target_id"); + + b.ToTable("legacy_entity_externals", (string)null); + }); + + modelBuilder.Entity("Pf2eModel.Model.Base.Legacy.LegacyEntitySource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id") + .HasColumnOrder(0) + .HasComment("Идентификатор"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("IsRemaster") + .HasColumnType("boolean") + .HasColumnName("is_remaster") + .HasColumnOrder(4) + .HasComment("Флаг, указывающий, что объект является частью ремастера"); + + b.Property("LegacyEntityId") + .HasColumnType("integer") + .HasColumnName("legacy_entity_id") + .HasColumnOrder(1) + .HasComment("Идентификатор устаревшей сущности"); + + b.Property("SourceId") + .HasColumnType("integer") + .HasColumnName("source_id") + .HasColumnOrder(2) + .HasComment("Идентификатор источника"); + + b.Property("Suffix") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("suffix") + .HasColumnOrder(3) + .HasComment("Номер страницы, эррата и т.д."); + + b.HasKey("Id") + .HasName("pk_legacy_entity_sources"); + + b.HasIndex("LegacyEntityId") + .HasDatabaseName("ix_legacy_entity_sources_legacy_entity_id"); + + b.HasIndex("SourceId") + .HasDatabaseName("ix_legacy_entity_sources_source_id"); + + b.ToTable("legacy_entity_sources", (string)null); + }); + modelBuilder.Entity("Pf2eModel.Model.Base.Source.EntitySource", b => { b.Property("Id") @@ -340,6 +476,12 @@ namespace Pf2eModel.Migrations .HasColumnOrder(2) .HasComment("Идентификатор цели"); + b.Property("ExternalId") + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("external_id") + .HasColumnOrder(3); + b.Property("Md5") .IsRequired() .HasMaxLength(32) @@ -1842,6 +1984,23 @@ namespace Pf2eModel.Migrations .HasConstraintName("fk_entity_keyword_keywords_keywords_id"); }); + modelBuilder.Entity("EntityLegacyEntity", b => + { + b.HasOne("Pf2eModel.Model.Base.Legacy.LegacyEntity", null) + .WithMany() + .HasForeignKey("LegacyEntitiesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_entity_legacy_entity_legacy_entities_legacy_entities_id"); + + b.HasOne("Pf2eModel.Model.Base.Entity", null) + .WithMany() + .HasForeignKey("RemasterEntitiesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_entity_legacy_entity_entities_remaster_entities_id"); + }); + modelBuilder.Entity("EntityTrait", b => { b.HasOne("Pf2eModel.Model.Base.Entity", null) @@ -1871,6 +2030,48 @@ namespace Pf2eModel.Migrations b.Navigation("Keyword"); }); + modelBuilder.Entity("Pf2eModel.Model.Base.Legacy.LegacyEntityExternal", b => + { + b.HasOne("Pf2eModel.Model.Base.Legacy.LegacyEntity", "LegacyEntity") + .WithMany("Externals") + .HasForeignKey("LegacyEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_legacy_entity_externals_legacy_entities_legacy_entity_id"); + + b.HasOne("Pf2eModel.Model.Base.Targets.ExternalTarget", "Target") + .WithMany("LegacyEntityExternals") + .HasForeignKey("TargetId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_legacy_entity_externals_external_targets_target_id"); + + b.Navigation("LegacyEntity"); + + b.Navigation("Target"); + }); + + modelBuilder.Entity("Pf2eModel.Model.Base.Legacy.LegacyEntitySource", b => + { + b.HasOne("Pf2eModel.Model.Base.Legacy.LegacyEntity", "LegacyEntity") + .WithMany("Sources") + .HasForeignKey("LegacyEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_legacy_entity_sources_legacy_entities_legacy_entity_id"); + + b.HasOne("Pf2eModel.Model.Base.Source.Source", "Source") + .WithMany("LegacyEntitySources") + .HasForeignKey("SourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_legacy_entity_sources_sources_source_id"); + + b.Navigation("LegacyEntity"); + + b.Navigation("Source"); + }); + modelBuilder.Entity("Pf2eModel.Model.Base.Source.EntitySource", b => { b.HasOne("Pf2eModel.Model.Base.Entity", "Entity") @@ -2509,14 +2710,25 @@ namespace Pf2eModel.Migrations b.Navigation("Aliases"); }); + modelBuilder.Entity("Pf2eModel.Model.Base.Legacy.LegacyEntity", b => + { + b.Navigation("Externals"); + + b.Navigation("Sources"); + }); + modelBuilder.Entity("Pf2eModel.Model.Base.Source.Source", b => { b.Navigation("EntitySources"); + + b.Navigation("LegacyEntitySources"); }); modelBuilder.Entity("Pf2eModel.Model.Base.Targets.ExternalTarget", b => { b.Navigation("EntityExternals"); + + b.Navigation("LegacyEntityExternals"); }); modelBuilder.Entity("Pf2eModel.Model.Structure.Ancestry", b => diff --git a/Pf2eModel/Migrations/20250514084949_1.cs b/Pf2eModel/Migrations/20250514102154_1.cs similarity index 91% rename from Pf2eModel/Migrations/20250514084949_1.cs rename to Pf2eModel/Migrations/20250514102154_1.cs index dfcb326..fefb752 100644 --- a/Pf2eModel/Migrations/20250514084949_1.cs +++ b/Pf2eModel/Migrations/20250514102154_1.cs @@ -101,6 +101,19 @@ namespace Pf2eModel.Migrations table.PrimaryKey("pk_keywords", x => x.id); }); + migrationBuilder.CreateTable( + name: "legacy_entities", + columns: table => new + { + id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + name = table.Column(type: "jsonb", nullable: false, comment: "Отображаемое имя") + }, + constraints: table => + { + table.PrimaryKey("pk_legacy_entities", x => x.id); + }); + migrationBuilder.CreateTable( name: "sources", columns: table => new @@ -399,6 +412,7 @@ namespace Pf2eModel.Migrations { entity_id = table.Column(type: "integer", nullable: false, comment: "Идентификатор сущности"), target_id = table.Column(type: "integer", nullable: false, comment: "Идентификатор цели"), + external_id = table.Column(type: "character varying(255)", maxLength: 255, nullable: true), suffix = table.Column(type: "character varying(255)", maxLength: 255, nullable: false, comment: "Часть URL до объекта. Для источников Foundry - полный UUID объекта"), md5 = table.Column(type: "character varying(32)", maxLength: 32, nullable: false, comment: "Контрольная сумма объекта") }, @@ -463,6 +477,57 @@ namespace Pf2eModel.Migrations onDelete: ReferentialAction.Cascade); }); + migrationBuilder.CreateTable( + name: "entity_legacy_entity", + columns: table => new + { + legacy_entities_id = table.Column(type: "integer", nullable: false), + remaster_entities_id = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("pk_entity_legacy_entity", x => new { x.legacy_entities_id, x.remaster_entities_id }); + table.ForeignKey( + name: "fk_entity_legacy_entity_entities_remaster_entities_id", + column: x => x.remaster_entities_id, + principalTable: "entities", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "fk_entity_legacy_entity_legacy_entities_legacy_entities_id", + column: x => x.legacy_entities_id, + principalTable: "legacy_entities", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "legacy_entity_externals", + columns: table => new + { + legacy_entity_id = table.Column(type: "integer", nullable: false, comment: "Идентификатор сущности"), + target_id = table.Column(type: "integer", nullable: false, comment: "Идентификатор цели"), + external_id = table.Column(type: "character varying(255)", maxLength: 255, nullable: true), + suffix = table.Column(type: "character varying(255)", maxLength: 255, nullable: false, comment: "Часть URL до объекта. Для источников Foundry - полный UUID объекта"), + md5 = table.Column(type: "character varying(32)", maxLength: 32, nullable: false, comment: "Контрольная сумма объекта") + }, + constraints: table => + { + table.PrimaryKey("pk_legacy_entity_externals", x => new { x.legacy_entity_id, x.target_id }); + table.ForeignKey( + name: "fk_legacy_entity_externals_external_targets_target_id", + column: x => x.target_id, + principalTable: "external_targets", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "fk_legacy_entity_externals_legacy_entities_legacy_entity_id", + column: x => x.legacy_entity_id, + principalTable: "legacy_entities", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + }); + migrationBuilder.CreateTable( name: "entity_sources", columns: table => new @@ -491,6 +556,34 @@ namespace Pf2eModel.Migrations onDelete: ReferentialAction.Cascade); }); + migrationBuilder.CreateTable( + name: "legacy_entity_sources", + columns: table => new + { + id = table.Column(type: "integer", nullable: false, comment: "Идентификатор") + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + legacy_entity_id = table.Column(type: "integer", nullable: false, comment: "Идентификатор устаревшей сущности"), + source_id = table.Column(type: "integer", nullable: false, comment: "Идентификатор источника"), + suffix = table.Column(type: "character varying(255)", maxLength: 255, nullable: false, comment: "Номер страницы, эррата и т.д."), + is_remaster = table.Column(type: "boolean", nullable: false, comment: "Флаг, указывающий, что объект является частью ремастера") + }, + constraints: table => + { + table.PrimaryKey("pk_legacy_entity_sources", x => x.id); + table.ForeignKey( + name: "fk_legacy_entity_sources_legacy_entities_legacy_entity_id", + column: x => x.legacy_entity_id, + principalTable: "legacy_entities", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "fk_legacy_entity_sources_sources_source_id", + column: x => x.source_id, + principalTable: "sources", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + }); + migrationBuilder.CreateTable( name: "ancestry_boosts", columns: table => new @@ -1260,6 +1353,11 @@ namespace Pf2eModel.Migrations table: "entity_keyword", column: "keywords_id"); + migrationBuilder.CreateIndex( + name: "ix_entity_legacy_entity_remaster_entities_id", + table: "entity_legacy_entity", + column: "remaster_entities_id"); + migrationBuilder.CreateIndex( name: "ix_entity_sources_entity_id", table: "entity_sources", @@ -1295,6 +1393,21 @@ namespace Pf2eModel.Migrations table: "languages", column: "entity_id"); + migrationBuilder.CreateIndex( + name: "ix_legacy_entity_externals_target_id", + table: "legacy_entity_externals", + column: "target_id"); + + migrationBuilder.CreateIndex( + name: "ix_legacy_entity_sources_legacy_entity_id", + table: "legacy_entity_sources", + column: "legacy_entity_id"); + + migrationBuilder.CreateIndex( + name: "ix_legacy_entity_sources_source_id", + table: "legacy_entity_sources", + column: "source_id"); + migrationBuilder.CreateIndex( name: "ix_ritual_checks_ritual_id", table: "ritual_checks", @@ -1411,6 +1524,9 @@ namespace Pf2eModel.Migrations migrationBuilder.DropTable( name: "entity_keyword"); + migrationBuilder.DropTable( + name: "entity_legacy_entity"); + migrationBuilder.DropTable( name: "entity_sources"); @@ -1429,6 +1545,12 @@ namespace Pf2eModel.Migrations migrationBuilder.DropTable( name: "keyword_aliases"); + migrationBuilder.DropTable( + name: "legacy_entity_externals"); + + migrationBuilder.DropTable( + name: "legacy_entity_sources"); + migrationBuilder.DropTable( name: "ritual_areas"); @@ -1465,12 +1587,6 @@ namespace Pf2eModel.Migrations migrationBuilder.DropTable( name: "deities"); - migrationBuilder.DropTable( - name: "external_targets"); - - migrationBuilder.DropTable( - name: "sources"); - migrationBuilder.DropTable( name: "traits"); @@ -1480,6 +1596,15 @@ namespace Pf2eModel.Migrations migrationBuilder.DropTable( name: "keywords"); + migrationBuilder.DropTable( + name: "external_targets"); + + migrationBuilder.DropTable( + name: "legacy_entities"); + + migrationBuilder.DropTable( + name: "sources"); + migrationBuilder.DropTable( name: "rituals"); diff --git a/Pf2eModel/Migrations/Pf2eDbContextModelSnapshot.cs b/Pf2eModel/Migrations/Pf2eDbContextModelSnapshot.cs index 1dcf56e..77fe09f 100644 --- a/Pf2eModel/Migrations/Pf2eDbContextModelSnapshot.cs +++ b/Pf2eModel/Migrations/Pf2eDbContextModelSnapshot.cs @@ -83,6 +83,25 @@ namespace Pf2eModel.Migrations b.ToTable("entity_keyword", (string)null); }); + modelBuilder.Entity("EntityLegacyEntity", b => + { + b.Property("LegacyEntitiesId") + .HasColumnType("integer") + .HasColumnName("legacy_entities_id"); + + b.Property("RemasterEntitiesId") + .HasColumnType("integer") + .HasColumnName("remaster_entities_id"); + + b.HasKey("LegacyEntitiesId", "RemasterEntitiesId") + .HasName("pk_entity_legacy_entity"); + + b.HasIndex("RemasterEntitiesId") + .HasDatabaseName("ix_entity_legacy_entity_remaster_entities_id"); + + b.ToTable("entity_legacy_entity", (string)null); + }); + modelBuilder.Entity("EntityTrait", b => { b.Property("EntitiesId") @@ -237,6 +256,123 @@ namespace Pf2eModel.Migrations b.ToTable("keyword_aliases", (string)null); }); + modelBuilder.Entity("Pf2eModel.Model.Base.Legacy.LegacyEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id") + .HasColumnOrder(1); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasColumnType("jsonb") + .HasColumnName("name") + .HasColumnOrder(2) + .HasComment("Отображаемое имя"); + + b.HasKey("Id") + .HasName("pk_legacy_entities"); + + b.ToTable("legacy_entities", (string)null); + }); + + modelBuilder.Entity("Pf2eModel.Model.Base.Legacy.LegacyEntityExternal", b => + { + b.Property("LegacyEntityId") + .HasColumnType("integer") + .HasColumnName("legacy_entity_id") + .HasColumnOrder(1) + .HasComment("Идентификатор сущности"); + + b.Property("TargetId") + .HasColumnType("integer") + .HasColumnName("target_id") + .HasColumnOrder(2) + .HasComment("Идентификатор цели"); + + b.Property("ExternalId") + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("external_id") + .HasColumnOrder(3); + + b.Property("Md5") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("character varying(32)") + .HasColumnName("md5") + .HasColumnOrder(5) + .HasComment("Контрольная сумма объекта"); + + b.Property("Suffix") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("suffix") + .HasColumnOrder(4) + .HasComment("Часть URL до объекта. Для источников Foundry - полный UUID объекта"); + + b.HasKey("LegacyEntityId", "TargetId") + .HasName("pk_legacy_entity_externals"); + + b.HasIndex("TargetId") + .HasDatabaseName("ix_legacy_entity_externals_target_id"); + + b.ToTable("legacy_entity_externals", (string)null); + }); + + modelBuilder.Entity("Pf2eModel.Model.Base.Legacy.LegacyEntitySource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id") + .HasColumnOrder(0) + .HasComment("Идентификатор"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("IsRemaster") + .HasColumnType("boolean") + .HasColumnName("is_remaster") + .HasColumnOrder(4) + .HasComment("Флаг, указывающий, что объект является частью ремастера"); + + b.Property("LegacyEntityId") + .HasColumnType("integer") + .HasColumnName("legacy_entity_id") + .HasColumnOrder(1) + .HasComment("Идентификатор устаревшей сущности"); + + b.Property("SourceId") + .HasColumnType("integer") + .HasColumnName("source_id") + .HasColumnOrder(2) + .HasComment("Идентификатор источника"); + + b.Property("Suffix") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("suffix") + .HasColumnOrder(3) + .HasComment("Номер страницы, эррата и т.д."); + + b.HasKey("Id") + .HasName("pk_legacy_entity_sources"); + + b.HasIndex("LegacyEntityId") + .HasDatabaseName("ix_legacy_entity_sources_legacy_entity_id"); + + b.HasIndex("SourceId") + .HasDatabaseName("ix_legacy_entity_sources_source_id"); + + b.ToTable("legacy_entity_sources", (string)null); + }); + modelBuilder.Entity("Pf2eModel.Model.Base.Source.EntitySource", b => { b.Property("Id") @@ -337,6 +473,12 @@ namespace Pf2eModel.Migrations .HasColumnOrder(2) .HasComment("Идентификатор цели"); + b.Property("ExternalId") + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("external_id") + .HasColumnOrder(3); + b.Property("Md5") .IsRequired() .HasMaxLength(32) @@ -1839,6 +1981,23 @@ namespace Pf2eModel.Migrations .HasConstraintName("fk_entity_keyword_keywords_keywords_id"); }); + modelBuilder.Entity("EntityLegacyEntity", b => + { + b.HasOne("Pf2eModel.Model.Base.Legacy.LegacyEntity", null) + .WithMany() + .HasForeignKey("LegacyEntitiesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_entity_legacy_entity_legacy_entities_legacy_entities_id"); + + b.HasOne("Pf2eModel.Model.Base.Entity", null) + .WithMany() + .HasForeignKey("RemasterEntitiesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_entity_legacy_entity_entities_remaster_entities_id"); + }); + modelBuilder.Entity("EntityTrait", b => { b.HasOne("Pf2eModel.Model.Base.Entity", null) @@ -1868,6 +2027,48 @@ namespace Pf2eModel.Migrations b.Navigation("Keyword"); }); + modelBuilder.Entity("Pf2eModel.Model.Base.Legacy.LegacyEntityExternal", b => + { + b.HasOne("Pf2eModel.Model.Base.Legacy.LegacyEntity", "LegacyEntity") + .WithMany("Externals") + .HasForeignKey("LegacyEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_legacy_entity_externals_legacy_entities_legacy_entity_id"); + + b.HasOne("Pf2eModel.Model.Base.Targets.ExternalTarget", "Target") + .WithMany("LegacyEntityExternals") + .HasForeignKey("TargetId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_legacy_entity_externals_external_targets_target_id"); + + b.Navigation("LegacyEntity"); + + b.Navigation("Target"); + }); + + modelBuilder.Entity("Pf2eModel.Model.Base.Legacy.LegacyEntitySource", b => + { + b.HasOne("Pf2eModel.Model.Base.Legacy.LegacyEntity", "LegacyEntity") + .WithMany("Sources") + .HasForeignKey("LegacyEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_legacy_entity_sources_legacy_entities_legacy_entity_id"); + + b.HasOne("Pf2eModel.Model.Base.Source.Source", "Source") + .WithMany("LegacyEntitySources") + .HasForeignKey("SourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_legacy_entity_sources_sources_source_id"); + + b.Navigation("LegacyEntity"); + + b.Navigation("Source"); + }); + modelBuilder.Entity("Pf2eModel.Model.Base.Source.EntitySource", b => { b.HasOne("Pf2eModel.Model.Base.Entity", "Entity") @@ -2506,14 +2707,25 @@ namespace Pf2eModel.Migrations b.Navigation("Aliases"); }); + modelBuilder.Entity("Pf2eModel.Model.Base.Legacy.LegacyEntity", b => + { + b.Navigation("Externals"); + + b.Navigation("Sources"); + }); + modelBuilder.Entity("Pf2eModel.Model.Base.Source.Source", b => { b.Navigation("EntitySources"); + + b.Navigation("LegacyEntitySources"); }); modelBuilder.Entity("Pf2eModel.Model.Base.Targets.ExternalTarget", b => { b.Navigation("EntityExternals"); + + b.Navigation("LegacyEntityExternals"); }); modelBuilder.Entity("Pf2eModel.Model.Structure.Ancestry", b => diff --git a/Pf2eModel/Model/Base/Entity.cs b/Pf2eModel/Model/Base/Entity.cs index 429e1c8..89cff39 100644 --- a/Pf2eModel/Model/Base/Entity.cs +++ b/Pf2eModel/Model/Base/Entity.cs @@ -2,6 +2,7 @@ using System.ComponentModel.DataAnnotations.Schema; using Microsoft.EntityFrameworkCore; using Pf2eModel.Model.Base.Keywords; +using Pf2eModel.Model.Base.Legacy; using Pf2eModel.Model.Base.Source; using Pf2eModel.Model.Base.Targets; using Pf2eModel.Model.Common.I18n; @@ -99,4 +100,9 @@ public class Entity /// Коллекция внешних ссылок, связанных с сущностью. /// public ICollection Externals { get; set; } = []; + + /// + /// Коллекция устаревших сущностей. + /// + public IEnumerable LegacyEntities { get; set; } = []; } diff --git a/Pf2eModel/Model/Base/Legacy/LegacyEntity.cs b/Pf2eModel/Model/Base/Legacy/LegacyEntity.cs new file mode 100644 index 0000000..af1251a --- /dev/null +++ b/Pf2eModel/Model/Base/Legacy/LegacyEntity.cs @@ -0,0 +1,42 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using Microsoft.EntityFrameworkCore; +using Pf2eModel.Model.Common.I18n; + +namespace Pf2eModel.Model.Base.Legacy; + +/// +/// Информация об устаревшей сущности +/// +public class LegacyEntity +{ + /// + /// Идентификатор сущности. + /// + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + [Column(Order = 1)] + public int Id { get; set; } + + /// + /// Отображаемое имя сущности. + /// + [Column(Order = 2)] + [Comment("Отображаемое имя")] + public LocaleString Name { get; set; } = []; + + /// + /// Коллекция обновлённых сущностей. + /// + public ICollection RemasterEntities { get; set; } = []; + + /// + /// Коллекция источников, связанных с сущностью. + /// + public ICollection Sources { get; set; } = []; + + /// + /// Коллекция внешних ссылок, связанных с сущностью. + /// + public ICollection Externals { get; set; } = []; +} diff --git a/Pf2eModel/Model/Base/Legacy/LegacyEntityExternal.cs b/Pf2eModel/Model/Base/Legacy/LegacyEntityExternal.cs new file mode 100644 index 0000000..962635d --- /dev/null +++ b/Pf2eModel/Model/Base/Legacy/LegacyEntityExternal.cs @@ -0,0 +1,58 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using Microsoft.EntityFrameworkCore; +using Pf2eModel.Model.Base.Targets; + +namespace Pf2eModel.Model.Base.Legacy; + +/// +/// Внешняя ссылка сущности. +/// +[PrimaryKey(nameof(LegacyEntityId), nameof(TargetId))] +public class LegacyEntityExternal +{ + /// + /// Идентификатор сущности. + /// + [Column(Order = 1)] + [Comment("Идентификатор сущности")] + public int LegacyEntityId { get; set; } + + /// + /// Идентификатор цели. + /// + [Column(Order = 2)] + [Comment("Идентификатор цели")] + public int TargetId { get; set; } + + [Column(Order = 3)] + [MaxLength(255)] + public string? ExternalId { get; set; } = null; + + /// + /// Конечная часть URL внешней ссылки. + /// Для источников Foundry - полный UUID объекта. + /// + [Column(Order = 4)] + [MaxLength(255)] + [Comment("Часть URL до объекта. Для источников Foundry - полный UUID объекта")] + public string Suffix { get; set; } = ""; + + /// + /// Контрольная сумма объекта. + /// + [Column(Order = 5)] + [MaxLength(32)] + [Comment("Контрольная сумма объекта")] + public string Md5 { get; set; } = null!; + + /// + /// Сущность, которая ссылается на внешнюю ссылку. + /// + public LegacyEntity LegacyEntity { get; set; } = null!; + + /// + /// Цель внешней ссылки. + /// + public ExternalTarget Target { get; set; } = null!; +} diff --git a/Pf2eModel/Model/Base/Legacy/LegacyEntitySource.cs b/Pf2eModel/Model/Base/Legacy/LegacyEntitySource.cs new file mode 100644 index 0000000..a3d959d --- /dev/null +++ b/Pf2eModel/Model/Base/Legacy/LegacyEntitySource.cs @@ -0,0 +1,59 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using Microsoft.EntityFrameworkCore; + +namespace Pf2eModel.Model.Base.Legacy; + +/// +/// Связь между сущностью и источником. +/// +public class LegacyEntitySource +{ + /// + /// Идентификатор связи. + /// + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + [Column(Order = 0)] + [Comment("Идентификатор")] + public int Id { get; set; } + + /// + /// Идентификатор устаревшей сущности. + /// + [Column(Order = 1)] + [Comment("Идентификатор устаревшей сущности")] + public int LegacyEntityId { get; set; } + + /// + /// Идентификатор источника. + /// + [Column(Order = 2)] + [Comment("Идентификатор источника")] + public int SourceId { get; set; } + + /// + /// Дополнительная информация о источнике, например, номер страницы, номер тома и т.д. + /// + [Column(Order = 3)] + [MaxLength(255)] + [Comment("Номер страницы, эррата и т.д.")] + public string Suffix { get; set; } = null!; + + /// + /// Флаг, указывающий, что объект является частью ремастера. + /// + [Column(Order = 4)] + [Comment("Флаг, указывающий, что объект является частью ремастера")] + public bool IsRemaster { get; set; } + + /// + /// Сущность, к которой относится источник. + /// + public LegacyEntity LegacyEntity { get; set; } = null!; + + /// + /// Источник, к которому относится сущность. + /// + public Source.Source Source { get; set; } = null!; +} diff --git a/Pf2eModel/Model/Base/Source/Source.cs b/Pf2eModel/Model/Base/Source/Source.cs index 7f098f1..6a88179 100644 --- a/Pf2eModel/Model/Base/Source/Source.cs +++ b/Pf2eModel/Model/Base/Source/Source.cs @@ -1,6 +1,7 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using Microsoft.EntityFrameworkCore; +using Pf2eModel.Model.Base.Legacy; using Pf2eModel.Model.Enum; namespace Pf2eModel.Model.Base.Source; @@ -45,4 +46,9 @@ public class Source /// Связанные сущности. /// public ICollection EntitySources { get; set; } = []; + + /// + /// Связанные сущности. + /// + public ICollection LegacyEntitySources { get; set; } = []; } diff --git a/Pf2eModel/Model/Base/Targets/EntityExternal.cs b/Pf2eModel/Model/Base/Targets/EntityExternal.cs index a662e50..56f4951 100644 --- a/Pf2eModel/Model/Base/Targets/EntityExternal.cs +++ b/Pf2eModel/Model/Base/Targets/EntityExternal.cs @@ -24,11 +24,9 @@ public class EntityExternal [Comment("Идентификатор цели")] public int TargetId { get; set; } - /* [Column(Order = 3)] [MaxLength(255)] - public string ExternalId { get; set; } = ""; - */ + public string? ExternalId { get; set; } = null; /// /// Конечная часть URL внешней ссылки. diff --git a/Pf2eModel/Model/Base/Targets/ExternalTarget.cs b/Pf2eModel/Model/Base/Targets/ExternalTarget.cs index 44b74bb..6602b5b 100644 --- a/Pf2eModel/Model/Base/Targets/ExternalTarget.cs +++ b/Pf2eModel/Model/Base/Targets/ExternalTarget.cs @@ -1,6 +1,7 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using Microsoft.EntityFrameworkCore; +using Pf2eModel.Model.Base.Legacy; namespace Pf2eModel.Model.Base.Targets; @@ -46,4 +47,9 @@ public class ExternalTarget /// Коллекция сущностей, которые ссылаются на эту цель. /// public ICollection EntityExternals { get; set; } = []; + + /// + /// Коллекция устаревших сущностей, которые ссылаются на эту цель. + /// + public ICollection LegacyEntityExternals { get; set; } = []; } diff --git a/Pf2eModel/Pf2eDbContext.cs b/Pf2eModel/Pf2eDbContext.cs index 4bcf998..2ecf8c7 100644 --- a/Pf2eModel/Pf2eDbContext.cs +++ b/Pf2eModel/Pf2eDbContext.cs @@ -3,6 +3,7 @@ using dotenv.net.Utilities; using Microsoft.EntityFrameworkCore; using Pf2eModel.Model.Base; using Pf2eModel.Model.Base.Keywords; +using Pf2eModel.Model.Base.Legacy; using Pf2eModel.Model.Base.Source; using Pf2eModel.Model.Base.Targets; using Pf2eModel.Model.Common; @@ -97,6 +98,12 @@ public partial class Pf2eDbContext : DbContext public virtual DbSet Languages { get; set; } + public virtual DbSet LegacyEntities { get; set; } + + public virtual DbSet LegacyEntityExternals { get; set; } + + public virtual DbSet LegacyEntitySources { get; set; } + public virtual DbSet Rituals { get; set; } public virtual DbSet RitualAreas { get; set; } diff --git a/Pf2eModel/Pf2eDbContextModel.cs b/Pf2eModel/Pf2eDbContextModel.cs index 56a8917..94afdf3 100644 --- a/Pf2eModel/Pf2eDbContextModel.cs +++ b/Pf2eModel/Pf2eDbContextModel.cs @@ -6,6 +6,7 @@ using Microsoft.EntityFrameworkCore.Metadata.Builders; using Pf2eModel.Model; using Pf2eModel.Model.Base; using Pf2eModel.Model.Base.Keywords; +using Pf2eModel.Model.Base.Legacy; using Pf2eModel.Model.Base.Source; using Pf2eModel.Model.Base.Targets; using Pf2eModel.Model.Common; @@ -144,6 +145,28 @@ public partial class Pf2eDbContext .OnDelete(DeleteBehavior.Cascade); }); + // LegacyEntity + modelBuilder.Entity(entity => + { + BindLocaleString(entity, l => l.Name); + + entity.HasMany(l => l.RemasterEntities).WithMany(e => e.LegacyEntities); + + // LegacyEntitySource + entity + .HasMany(e => e.Sources) + .WithOne(e => e.LegacyEntity) + .HasForeignKey(e => e.LegacyEntityId) + .OnDelete(DeleteBehavior.Cascade); + + // LegacyEntityExternal + entity + .HasMany(e => e.Externals) + .WithOne(e => e.LegacyEntity) + .HasForeignKey(e => e.LegacyEntityId) + .OnDelete(DeleteBehavior.Cascade); + }); + // Source modelBuilder.Entity(entity => { @@ -155,6 +178,12 @@ public partial class Pf2eDbContext .HasForeignKey(e => e.SourceId) .OnDelete(DeleteBehavior.Cascade); + entity + .HasMany(s => s.LegacyEntitySources) + .WithOne(e => e.Source) + .HasForeignKey(e => e.SourceId) + .OnDelete(DeleteBehavior.Cascade); + // + EntitySource }); modelBuilder.Entity(entity => { }); @@ -167,6 +196,12 @@ public partial class Pf2eDbContext .WithOne(e => e.Target) .HasForeignKey(e => e.TargetId) .OnDelete(DeleteBehavior.Cascade); + + entity + .HasMany(t => t.LegacyEntityExternals) + .WithOne(e => e.Target) + .HasForeignKey(e => e.TargetId) + .OnDelete(DeleteBehavior.Cascade); }); modelBuilder.Entity(entity => { });