From 53baa87144ec366fd20d82eed019baa0afeff285 Mon Sep 17 00:00:00 2001 From: Ilya Kozhemyakin Date: Sun, 11 May 2025 23:47:07 +0400 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=BA=D1=83=D0=BC=D0=B5=D0=BD?= =?UTF-8?q?=D1=82=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D0=B5=20=D1=81?= =?UTF-8?q?=D0=BA=D1=80=D0=B0=D0=BF=D0=BF=D0=B5=D1=80=D0=B0.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Pf2eDataScrapper/EnvSettings.cs | 5 +- .../Importers/AbstractImporter.cs | 3 + Pf2eDataScrapper/Importers/DatabaseHelper.cs | 39 +++++- .../Implementations/Aonprd/AonprdImporter.cs | 24 +++- .../Implementations/Aonprd/AonprdLoader.cs | 44 +++++- .../Aonprd/AonprdRepository.cs | 22 ++- .../Aonprd/Compendium/AonprdCompendium.cs | 27 +++- .../Aonprd/Compendium/AonprdReference.cs | 29 +++- .../Aonprd/Compendium/AonprdType.cs | 21 ++- .../Aonprd/DataBuilder/AonprdDataBuilder.cs | 36 ++++- .../DataBuilder/AonprdProcessorAttribute.cs | 35 +++++ .../DataBuilder/AonprdProcessorCollection.cs | 47 ++++++- .../DataBuilder/AonprdProcessorInput.cs | 48 ++++++- .../Helpers/AonprdBuilderGlobals.cs | 13 ++ .../DataBuilder/Helpers/AonprdEnumResolver.cs | 33 +++++ .../DataBuilder/Helpers/StringHelper.cs | 66 ++++++++- .../Aonprd/DataBuilder/LegacyEntityHandler.cs | 40 +++++- .../Processors/AbstractEntityProcessor.cs | 104 ++++++++++++-- .../Processors/AbstractProcessor.cs | 28 ++++ .../Processors/AbstractSubclassProcessor.cs | 27 +++- .../Processors/Common/ExternalProcessor.cs | 2 +- .../Processors/Common/SkillProcessor.cs | 12 +- .../Processors/Common/SourceProcessor.cs | 5 + .../Subclasses/ApparitionProcessor.cs | 11 +- .../Processors/Types/ActionProcessor.cs | 30 ++-- .../Processors/Types/AncestryProcessor.cs | 4 + .../Processors/Types/ArchetypeProcessor.cs | 10 +- .../Processors/Types/BackgroundProcessor.cs | 12 +- .../Processors/Types/ClassProcessor.cs | 56 ++++++-- .../Processors/Types/FeatProcessor.cs | 7 +- .../Processors/Types/LanguageProcessor.cs | 3 + .../Processors/Types/RitualProcessor.cs | 14 +- .../Processors/Types/Rules/RuleProcessor.cs | 22 ++- .../Processors/Types/Rules/RuleToc.cs | 130 +++++++++++++++++- .../Types/Rules/RulesOrderProcessor.cs | 46 ++++++- .../Types/Rules/RulesTocProcessor.cs | 30 +++- .../Processors/Types/SpellProcessor.cs | 43 +++--- .../Processors/Types/TraitProcessor.cs | 6 + .../SchemaBuilder/AonprdSchemaBuilder.cs | 38 ++++- Pf2eDataScrapper/Importers/ProcessorInput.cs | 59 +++++++- .../AbstractImporterRepository.cs | 14 ++ .../Importers/Repositories/GitRepository.cs | 24 ++-- Pf2eDataScrapper/Pf2eDatabase.cs | 40 +++++- Pf2eDataScrapper/Program.cs | 2 +- 44 files changed, 1152 insertions(+), 159 deletions(-) diff --git a/Pf2eDataScrapper/EnvSettings.cs b/Pf2eDataScrapper/EnvSettings.cs index dd1b457..f5b02c1 100644 --- a/Pf2eDataScrapper/EnvSettings.cs +++ b/Pf2eDataScrapper/EnvSettings.cs @@ -6,7 +6,7 @@ namespace Pf2eDataScrapper; /// /// Класс настроек. /// -internal static class EnvSettings +public static class EnvSettings { /// /// Загрузка файла настроек `.env`. @@ -36,5 +36,8 @@ internal static class EnvSettings /// public static string OutputDir => new(OutputDirPath); + /// + /// Строка подключения к базе данных. + /// public static string DatabaseConnection => EnvReader.GetStringValue("DATABASE_CONNECTION"); } diff --git a/Pf2eDataScrapper/Importers/AbstractImporter.cs b/Pf2eDataScrapper/Importers/AbstractImporter.cs index e80020e..fcddf09 100644 --- a/Pf2eDataScrapper/Importers/AbstractImporter.cs +++ b/Pf2eDataScrapper/Importers/AbstractImporter.cs @@ -17,6 +17,9 @@ internal abstract class AbstractImporter /// public abstract string Slug { get; } + /// + /// Репозиторий для данных, загруженных из необходимых источников. + /// public abstract AbstractImporterRepository Repository { get; } /// diff --git a/Pf2eDataScrapper/Importers/DatabaseHelper.cs b/Pf2eDataScrapper/Importers/DatabaseHelper.cs index 718aed3..420a102 100644 --- a/Pf2eDataScrapper/Importers/DatabaseHelper.cs +++ b/Pf2eDataScrapper/Importers/DatabaseHelper.cs @@ -1,33 +1,60 @@ -using Microsoft.EntityFrameworkCore; -using Pf2eModel; +using Pf2eModel; using Slugify; namespace Pf2eDataScrapper.Importers; +/// +/// Класс предоставляет методы для работы с базой данных. +/// public class DatabaseHelper { - public Pf2eDbContext Context = new(); - - public readonly SlugHelper Slugger = new(); - + /// + /// Последний найденный идентификатор сущности. + /// Используется для генерации новых ID сущностей. + /// private int _id; + /// + /// Генерирует новый уникальный идентификатор сущности. и возвращает его. + /// public int NextEntityId => ++_id; + /// + /// Сервис для работы с URL-Slug. Используется для преобразования названий в URL-friendly формат. + /// + public readonly SlugHelper Slugger = new(); + + /// + /// Context для доступа к базе данных. + /// + public readonly Pf2eDbContext Context = new(); + private DatabaseHelper() { _id = Context.Entities.Any() ? Context.Entities.Max(e => e.Id) : 0; } + /// + /// Генерирует slug из названия. + /// public string GenerateSlug(string name) { return Slugger.GenerateSlug(name); } + /// + /// Возвращает singleton-инстанцию класса . + /// public static readonly DatabaseHelper Instance = new(); + /// + /// Контекст для доступа к базе данных. + /// public static Pf2eDbContext DbContext => Instance.Context; + /// + /// Обновляет контекст и сохраняет изменения в базе данных. + /// public static void ResetContext() { Instance.Context.SaveChanges(); diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/AonprdImporter.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/AonprdImporter.cs index 7559823..b6443dc 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/AonprdImporter.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/AonprdImporter.cs @@ -4,28 +4,42 @@ using Pf2eDataScrapper.Importers.Repositories; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd; +/// +/// Класс обработки данных с сайта Архивов Нефеса. +/// internal class AonprdImporter : AbstractImporter { - public override string Name => SourceName; + /// + public override string Name => TargetName; + /// public override string Slug => "aonprd"; + /// public override AbstractImporterRepository Repository => new AonprdRepository(this, "https://elasticsearch.aonprd.com"); + /// + /// Инструмент для работы с данными сайта Архивов Нефеса. + /// public AonprdLoader Loader { get; } + /// + /// Конструктор класса . + /// public AonprdImporter() { - Loader = new AonprdLoader(this); + Loader = new(this); } + /// public override void Prepare() { Repository.Update(); Loader.Prepare(); } + /// public override void BuildSchemas() { AonprdSchemaBuilder builder = new(Loader); @@ -34,6 +48,7 @@ internal class AonprdImporter : AbstractImporter builder.Save(EnvSettings.OutputDir); } + /// public override void Import() { AonprdDataBuilder builder = new(Loader); @@ -43,5 +58,8 @@ internal class AonprdImporter : AbstractImporter builder.Build(); } - public const string SourceName = "Archives of Nethys Pathfinder 2e"; + /// + /// Название внешней ссылки. + /// + public const string TargetName = "Archives of Nethys Pathfinder 2e"; } diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/AonprdLoader.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/AonprdLoader.cs index 6813181..d97b8ec 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/AonprdLoader.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/AonprdLoader.cs @@ -3,23 +3,37 @@ using Pf2eDataScrapper.Importers.Implementations.Aonprd.Compendium; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd; +/// +/// Класс загрузки данных с сайта Архивов Нефеса. +/// internal class AonprdLoader(AonprdImporter importer) { - public AonprdImporter Importer = importer; + /// + /// Референс на импортер, используемый для загрузки данных. + /// + public AonprdImporter Importer { get; } = importer; - public AonprdCompendium Compendium = new(); + /// + /// Компендиум данных с сайта Архивов Нефеса. + /// + public AonprdCompendium Compendium { get; } = new(); + /// + /// Число элементов в компендарии. + /// public int ItemCount => Compendium.Count(); + /// + /// Подготовка данных перед загрузкой. + /// public void Prepare() { using StreamReader indexFile = new(Path.Combine(Importer.Dir, "index.json")); JObject indexJson = JObject.Parse(indexFile.ReadToEnd()); - foreach (KeyValuePair kvp in indexJson) + foreach ((string file, JToken? value) in indexJson) { - string file = kvp.Key; - JArray items = (JArray)kvp.Value!; + JArray items = (JArray)value!; foreach (JToken item in items) { @@ -28,16 +42,30 @@ internal class AonprdLoader(AonprdImporter importer) } } + /// + /// Возвращает коллекцию всех доступных загружаемых элементов. + /// + /// Коллекция всех загружаемых элементов. public IEnumerable Entries() { return Entries([.. Compendium.Types.Keys]); } + /// + /// Возвращает коллекцию доступных загружаемых элементов. + /// + /// Тип, которые нужно включить в результат. + /// Коллекция загружаемых элементов, соответствующих предоставленному типу. public IEnumerable Entries(string types) { return Entries([types]); } + /// + /// Возвращает коллекцию доступных загружаемых элементов. + /// + /// Типы, которые нужно включить в результат. + /// Коллекция загружаемых элементов, соответствующих предоставленным типам. public IEnumerable Entries(string[] types) { foreach (string file in Compendium.Files(types)) @@ -64,8 +92,14 @@ internal class AonprdLoader(AonprdImporter importer) } } + /// + /// Класс, представляющий загружаемый элемент. + /// public class LoaderEntry(string id, string file, JObject itemJson) : AonprdReference(id, file) { + /// + /// Возвращает JSON-объект, содерждающий данные о элементе. + /// public JObject Json { get; } = itemJson; } } diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/AonprdRepository.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/AonprdRepository.cs index 4d21dd2..ba05d9b 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/AonprdRepository.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/AonprdRepository.cs @@ -4,13 +4,25 @@ using Pf2eDataScrapper.Importers.Repositories; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd; +/// +/// Основной класс для работы с репозиторием данных Aonprd. +/// internal class AonprdRepository(AbstractImporter importer, string searchUrl) : AbstractImporterRepository(importer) { + /// + /// URL для доступа к API Aonprd. + /// public readonly string SearchUrl = searchUrl; + /// + /// Текущая ревизия данных. Значение обновляется при обновлении репозитория. + /// public int Revision { get; private set; } + /// + /// Метод для загрузки данных из API Aonprd. + /// private string Load(string url) { if (!url.StartsWith("http://") && !url.StartsWith("https://")) @@ -25,6 +37,7 @@ internal class AonprdRepository(AbstractImporter importer, string searchUrl) return reader.ReadToEnd(); } + /// public override void Update() { if (CheckRevision()) @@ -69,9 +82,9 @@ internal class AonprdRepository(AbstractImporter importer, string searchUrl) } /// - /// Проверка актуальности данных + /// Проверка актуальности данных. /// - /// true - актуальная ревизия, false - устаревшая/отсутсвует + /// true - актуальная ревизия, false - устаревшая/отсутсвует. private bool CheckRevision() { JObject page = JObject.Parse(Load("/aon/_doc/feat-1")); @@ -92,6 +105,11 @@ internal class AonprdRepository(AbstractImporter importer, string searchUrl) return lastRevision == Revision.ToString(); } + /// + /// Запись данных в файл. + /// + /// Путь к файлу. + /// Содержимое, которе требуется записать в файл. private void WriteFile(string path, string content) { string filePath = Path.Combine(RepositoryDirPath, path); diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Compendium/AonprdCompendium.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Compendium/AonprdCompendium.cs index 86a4624..e7df834 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Compendium/AonprdCompendium.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Compendium/AonprdCompendium.cs @@ -1,9 +1,20 @@ namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.Compendium; +/// +/// Класс хранения данных компендиума. +/// internal class AonprdCompendium { + /// + /// Словарь данных компендиума. + /// public readonly Dictionary Types = []; + /// + /// Добавить ссылку на сущность с указанием файла, в котором она хранится. + /// + /// Идентификатор сущности. + /// Имя файла. public void AddReference(string id, string file) { AonprdReference reference = new(id, file); @@ -14,15 +25,29 @@ internal class AonprdCompendium } else { - Types.Add(reference.Type, new AonprdType(reference)); + Types.Add(reference.Type, new(reference)); } } + /// + /// Возвращает количество элементов в компендиуме. + /// + /// Количество элементов. public int Count() => Types.Values.Sum(t => t.Count); + /// + /// Возвращает количество элементов для указанных типов данных. + /// + /// Список типов. + /// Количество элементов. public int Count(IEnumerable types) => Types.Values.Where(t => types.Contains(t.Type)).Sum(t => t.Count); + /// + /// Возвращает список файлов для выбора сущностей указанных типов данных. + /// + /// Список типов. + /// Список искомых файлов. public IEnumerable Files(IEnumerable types) => Types.Values.Where(t => types.Contains(t.Type)).SelectMany(t => t.Files).Distinct(); } diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Compendium/AonprdReference.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Compendium/AonprdReference.cs index a53290f..98ebbdd 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Compendium/AonprdReference.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Compendium/AonprdReference.cs @@ -1,16 +1,37 @@ namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.Compendium; +/// +/// Ссылка на сущность с указанием файла, в котором она хранится. +/// +/// Идентификатор сущности. +/// Имя файла. internal class AonprdReference(string id, string file) { + /// + /// Идентификатор сущности. + /// public readonly string Id = id; + /// + /// Тип сущности. + /// public readonly string Type = ResolveType(id); + /// + /// Имя файла, в котором хранится сущность. + /// public readonly string File = file; - public static string ResolveType(string id) + /// + /// Определение типа сущности как префикса идентификатора. + /// + private static string ResolveType(string id) { - static string? subType(string type, string cat) + string type = string.Join('-', id.Split('-')[..^1]); + + return SubType(type, "equipment") ?? SubType(type, "weapon") ?? type; + + static string? SubType(string type, string cat) { return type.StartsWith($"{cat}-") && type != cat ? type.EndsWith("-bonus") @@ -18,9 +39,5 @@ internal class AonprdReference(string id, string file) : $"{cat}-sub" : null; } - - string type = string.Join('-', id.Split('-')[0..^1]); - - return subType(type, "equipment") ?? subType(type, "weapon") ?? type; } } diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Compendium/AonprdType.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Compendium/AonprdType.cs index 527d250..8f43ff7 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Compendium/AonprdType.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Compendium/AonprdType.cs @@ -1,9 +1,19 @@ namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.Compendium; +/// +/// Класс AonprdType представляет собой уникальный тип данных, связанный с Aon (Advanced Opportunity Network). +/// Это основной класс, который хранит информацию о различных элементах системы. +/// internal class AonprdType(string type) { + /// + /// Идентификатор типа данных. + /// public readonly string Type = type; + /// + /// Список ссылок на сущности, связанных с этим типом. + /// public readonly List Items = []; public AonprdType(AonprdReference reference) @@ -12,12 +22,21 @@ internal class AonprdType(string type) Items.Add(reference); } + /// + /// Добавляет новую ссылку в список. + /// public void Add(AonprdReference reference) { Items.Add(reference); } - public List Files => [.. Items.Select(x => x.File).Distinct()]; + /// + /// Возвращает уникальные файлы, использованные в ссылках. + /// + public List Files => Items.Select(x => x.File).Distinct().ToList(); + /// + /// Число элементов в списке ссылок. + /// public int Count => Items.Count; } diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/AonprdDataBuilder.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/AonprdDataBuilder.cs index 1fc0532..1f94777 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/AonprdDataBuilder.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/AonprdDataBuilder.cs @@ -5,12 +5,19 @@ using Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder; +/// +/// Класс для создание схемы данных Архива Нефиса. +/// +/// internal class AonprdDataBuilder(AonprdLoader loader) { + /// + /// Класс работы с базой данных. + /// public readonly Pf2eDatabase Database = Pf2eDatabase.Instance; /// - /// Загрузчик данных. + /// Основной класс для работы с данными Архива Нефиса. /// public readonly AonprdLoader Loader = loader; @@ -19,8 +26,14 @@ internal class AonprdDataBuilder(AonprdLoader loader) /// public AonprdCompendium Compendium => Loader.Compendium; + /// + /// Коллекция обработчиков данных. + /// public readonly AonprdProcessorCollection Processors = new(); + /// + /// Очистить таблицы в базе данных. + /// public void TruncateTables() { Console.WriteLine("Truncating all old data..."); @@ -30,14 +43,29 @@ internal class AonprdDataBuilder(AonprdLoader loader) Console.WriteLine("Truncation completed!"); } + /// + /// Конфигурация интерфейса вывода результатов. + /// protected ConcurrentWriter Window = null!; + /// + /// Основное окно для вывода информации. + /// protected Window Output = null!; + /// + /// Полоса прогресса для основного процесса обработки данных. + /// protected ProgressBar ProcessProgressBar = null!; + /// + /// Полоса прогресса для под-процессов обработки данных. + /// protected ProgressBar SubProcessProgressBar = null!; + /// + /// Метод для настройки интерфейса. + /// protected void ConfigureWindow() { Window = new Window(0, 0, Console.WindowWidth, Console.WindowHeight).Concurrent(); @@ -56,6 +84,9 @@ internal class AonprdDataBuilder(AonprdLoader loader) SubProcessProgressBar = new(bars, 1, 15); } + /// + /// Обработка отдельного процессора данных. + /// protected void RunProcessor(AonprdProcessorCollection.ProcessorItem item, JObject json) { AonprdProcessorInput input = new(json); @@ -76,6 +107,9 @@ internal class AonprdDataBuilder(AonprdLoader loader) } } + /// + /// Полная обработка и сохранение данных. + /// public void Build() { ConfigureWindow(); diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/AonprdProcessorAttribute.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/AonprdProcessorAttribute.cs index 05dcbc2..faac790 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/AonprdProcessorAttribute.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/AonprdProcessorAttribute.cs @@ -1,5 +1,12 @@ namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder; +/// +/// Атрибут обработчика данных Архивов Нефиса. +/// +/// Название обработчика. +/// Тип данных, которые обработчик обрабатывает. +/// Названия обработчиков, которые должны быть выполнены до запуска этого обработчика. +/// Флаг, указывающий, следует ли добавить важные общие зависимости для обработчка. [AttributeUsage(AttributeTargets.Class)] public class AonprdProcessorAttribute( string name, @@ -8,23 +15,51 @@ public class AonprdProcessorAttribute( bool includeDefaults = true ) : Attribute { + /// + /// Важные общие зависимости для обработчка. + /// private static readonly string[] DefaultDependencies = ["trait", "source"]; + + /// + /// Название обработчика. + /// public readonly string Name = name; + /// + /// Тип данных, которые обработчик обрабатывает. + /// public readonly string? Type = type; + /// + /// Названия обработчиков, которые должны быть выполнены до запуска этого обработчика. + /// public readonly IEnumerable Dependencies = [ .. includeDefaults ? DefaultDependencies : [], .. dependencies, ]; + /// + /// Конструктор класса со стандартными общими зависимости для обработчка. + /// + /// Тип данных, которые обработчик обрабатывает. Также будет использоваться в качестве названия. public AonprdProcessorAttribute(string type) : this(type, type, DefaultDependencies) { } + /// + /// Конструктор класса . + /// + /// Тип данных, которые обработчик обрабатывает. Также будет использоваться в качестве названия. + /// Названия обработчиков, которые должны быть выполнены до запуска этого обработчика. + /// Флаг, указывающий, следует ли добавить важные общие зависимости для обработчка. public AonprdProcessorAttribute(string type, string[] dependencies, bool includeDefaults = true) : this(type, type, dependencies, includeDefaults) { } + /// + /// Конструктор класса со стандартными общими зависимости для обработчка. + /// + /// Название обработчика. + /// Тип данных, которые обработчик обрабатывает. public AonprdProcessorAttribute(string name, string type) : this(name, type, DefaultDependencies) { } } diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/AonprdProcessorCollection.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/AonprdProcessorCollection.cs index 7f3d24a..9d66d8a 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/AonprdProcessorCollection.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/AonprdProcessorCollection.cs @@ -3,21 +3,47 @@ using Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder; +/// +/// Класс отвечает за управление обработчиками, отмеченных атрибутом . +/// internal class AonprdProcessorCollection { + /// + /// Элемент коллекции обработчика. + /// + /// Атрибут, связанный с обаботчиком. + /// Конструктор обработчика. public class ProcessorItem(AonprdProcessorAttribute attribute, ConstructorInfo constructor) { + /// + /// Атрибут, связанный с обаботчиком. + /// public readonly AonprdProcessorAttribute Attribute = attribute; + /// + /// Конструктор обработчика. + /// public readonly ConstructorInfo Constructor = constructor; + /// + /// Приоритет выполнения обработчика. + /// public int Priority = -1; } + /// + /// Список всех объявленных обработчиков. + /// private readonly List _allItems; + /// + /// Список используемых обработчиков. + /// public List Items { get; private set; } = []; + /// + /// Конструктор класса , загружающий все обработчики. + /// public AonprdProcessorCollection() { foreach (Type type in Assembly.GetExecutingAssembly().GetTypes()) @@ -29,7 +55,7 @@ internal class AonprdProcessorCollection if (!type.IsSubclassOf(typeof(AbstractProcessor))) { - throw new Exception( + throw new( $"'AonprdProcessor' attribute should only be applied to children of 'AbstractProcessor' class. '{type.FullName}' is not." ); } @@ -56,6 +82,9 @@ internal class AonprdProcessorCollection _allItems = Items.ToList(); } + /// + /// Проверка, что все зависимости всех обработчиков могут быть удовлетворены. + /// private void CheckDependencies() { foreach (ProcessorItem item in Items) @@ -72,6 +101,9 @@ internal class AonprdProcessorCollection } } + /// + /// Сортировать обработчики в соответсвии со всеми зависимостями. + /// private void SortItems() { CheckDependencies(); @@ -104,6 +136,10 @@ internal class AonprdProcessorCollection Items.Sort((a, b) => a.Priority - b.Priority); } + /// + /// Итерация через все используемые обработчики. + /// + /// Итератор. public IEnumerable Iterate() { SortItems(); @@ -114,9 +150,14 @@ internal class AonprdProcessorCollection } } - public void UseOnlyNames(string[] processorNames) + /// + /// Ограничить список используемых обработчиков по названию. + /// Автоматически включает все обработчики, от которых зависят указанные в аргументе обработчики. + /// + /// Коллекция названиий требуемых обработчиков. + public void UseOnlyNames(ICollection processorNames) { - if (processorNames.Length == 0) + if (processorNames.Count == 0) { Items = _allItems; return; diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/AonprdProcessorInput.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/AonprdProcessorInput.cs index 6f9aa5f..d369fec 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/AonprdProcessorInput.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/AonprdProcessorInput.cs @@ -3,8 +3,13 @@ using Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Helpers; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder; +/// +/// Класс для работы с JSON-объектом при обработки данных с Архивов Нефиса. +/// +/// JSON-объект, который будет использоваться для доступа к данным. internal class AonprdProcessorInput(JObject input) : ProcessorInput(input) { + /// public override T Enum(string value) { try @@ -17,6 +22,11 @@ internal class AonprdProcessorInput(JObject input) : ProcessorInput(input) } } + /// + /// Обработать строку, содержащую информацию о количестве действий. + /// + /// Входная строка с данными. + /// Структура, хранящая информацию о количетсве действий. public ActionStruct ResolveActionString(string input) { ActionStruct result = new(); @@ -89,13 +99,39 @@ internal class AonprdProcessorInput(JObject input) : ProcessorInput(input) return result; } + /// + /// Класс структуры, хранящей информацию о количетсве действий. + /// public class ActionStruct { - public int Actions; - public int MaxActions; - public bool IsPassive; - public bool IsReaction; - public bool IsFreeAction; - public bool TwoRounds; + /// + /// Количество действий + /// + public int Actions { get; set; } + + /// + /// Максимальное количество действий + /// + public int MaxActions { get; set; } + + /// + /// Флаг, указывающий, что действия не требуются. + /// + public bool IsPassive { get; set; } + + /// + /// Флаг, указывающий, что действие может быть использовано как реакция. + /// + public bool IsReaction { get; set; } + + /// + /// Флаг, указывающий, что можно использовать свободное действие. + /// + public bool IsFreeAction { get; set; } + + /// + /// Флаг, указывающий, что действие может быть продлено до 2 раундов. + /// + public bool TwoRounds { get; set; } } } diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Helpers/AonprdBuilderGlobals.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Helpers/AonprdBuilderGlobals.cs index 048c6be..def6837 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Helpers/AonprdBuilderGlobals.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Helpers/AonprdBuilderGlobals.cs @@ -2,11 +2,24 @@ namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Helpers; +/// +/// Класс-помощник, хранящий необходимые глобальные данные. +/// Реализует шаблон Singleton. +/// internal class AonprdBuilderGlobals { + /// + /// Цель внешних ссылок Архивов Нефисов. + /// public ExternalTarget AonprdTarget = null!; + /// + /// Конструктор класса . + /// private AonprdBuilderGlobals() { } + /// + /// Экземпляр класса . + /// public static readonly AonprdBuilderGlobals Instance = new(); } diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Helpers/AonprdEnumResolver.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Helpers/AonprdEnumResolver.cs index 809ead1..e4f395c 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Helpers/AonprdEnumResolver.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Helpers/AonprdEnumResolver.cs @@ -2,8 +2,14 @@ namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Helpers; +/// +/// Класс, помогающий разрешить строковые значения с Архивов Нефиса в перечисления. +/// public static class AonprdEnumResolver { + /// + /// Коллекция классов, разрешающих строковые значения в перечисления. + /// private static readonly ICollection Solvers = [ new Solver( @@ -62,6 +68,18 @@ public static class AonprdEnumResolver ), ]; + /// + /// Разрешить строковое значение с Архивов Нефиса в перечисление. + /// + /// Тип искомого перечисления. + /// Обрабатываемое строковоее значение. + /// Значение искомого перечисления. + /// + /// Исключение, если для указанного типа перечисления не указан класс, разрешающий строковые значения. + /// + /// + /// Исключение, если указанное значение не получилось преобразовать искомое значение. + /// public static T Resolve(string value) where T : Enum { @@ -81,11 +99,17 @@ public static class AonprdEnumResolver : throw new EnumResolverValueUnknownException(type, value); } + /// + /// Класс, разрешающий строковые значения в перечисления. + /// private abstract class Solver { public abstract Type Type { get; } } + /// + /// Абстрактный общий класс, разрешающий строковые значения в перечисления. + /// private class Solver(Dictionary data) : Solver where T : Enum { @@ -93,9 +117,18 @@ public static class AonprdEnumResolver public Dictionary Data { get; } = data; } + /// + /// Исключение, указывающее, что для указанного типа перечисления не указан класс, разрешающий строковые значения. + /// + /// Тип искомого перечисления. public class EnumResolverEnumUnknownException(Type t) : Exception($"Can't resolve type '{t.FullName}': Solver not defined"); + /// + /// Исключение, указывающее, что указанное значение не получилось преобразовать искомое значение. + /// + /// Тип искомого перечисления. + /// Искомое значение. public class EnumResolverValueUnknownException(Type t, string value) : Exception($"Can't resolve type '{t.FullName}': Value '{value}' is not matched"); } diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Helpers/StringHelper.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Helpers/StringHelper.cs index 384ea5f..7682c74 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Helpers/StringHelper.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Helpers/StringHelper.cs @@ -2,10 +2,22 @@ namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Helpers; +/// +/// Класс-помощник, для обработки строкового ввода с Архивов Нефиса. +/// Реализует шаблон Singleton. +/// internal partial class StringHelper { + /// + /// Конструктор класса + /// private StringHelper() { } + /// + /// Разбиение строки по нестабильным строковым разделителям (например, ", ", " or " и т.д.). + /// + /// Исходная строка. + /// Коллекция строк. public List RowSplit(string value) { return value @@ -17,6 +29,11 @@ internal partial class StringHelper .ToList(); } + /// + /// Разбор списка ссылок в исходной строке. + /// + /// Исходная строка. + /// Структура, содеражащая информацию разбиении строки по ссылкам. public MarkdownLinkSplit ParseMarkdownLinks(string input) { MarkdownLinkSplit result = new(); @@ -39,31 +56,70 @@ internal partial class StringHelper return result; } + /// + /// Извлечение информации о названии источника. + /// + /// Исходная строка. + /// Название источника. 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(); + /// + /// Регулярное выражение для извлечения источника из строки с Markdown-ссылкой. + /// + /// Регулярное выражение. [GeneratedRegex(@"^\[.*?\]\(.*?\)\s+(.*)$")] public static partial Regex SourceSuffixRegex(); + /// + /// Регулярное выражение для выявления Markdown-ссылок в строке. + /// + /// Регулярное выражение. [GeneratedRegex(@"(.*?)\[(.*?)\]\((.*?)\)")] public static partial Regex LinkRegex(); } +/// +/// Класс структуры, содеражащая информацию разбиении строки по ссылкам. +/// internal class MarkdownLinkSplit { - public List Links = []; + /// + /// Список содержащихся ссылок. + /// + public List Links { get; } = []; - public string Suffix = ""; + /// + /// Завершающая часть ссылки. + /// + public string Suffix { get; set; } = ""; } +/// +/// Информация о ссылке в строке. +/// internal class MarkdownLink { - public string Prefix { get; set; } = ""; - public string Text { get; set; } = ""; - public string Url { get; set; } = ""; + /// + /// Данные, содержащиеся до ссылки. + /// + public string Prefix { get; init; } = ""; + + /// + /// Текст ссылки. + /// + public string Text { get; init; } = ""; + + /// + /// URL ссылки. + /// + public string Url { get; init; } = ""; } diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/LegacyEntityHandler.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/LegacyEntityHandler.cs index e6f00d7..d5cfc57 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/LegacyEntityHandler.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/LegacyEntityHandler.cs @@ -1,29 +1,55 @@ -using Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Helpers; -using Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors; +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 LegacySources { get; private set; } = []; + /// + /// Флаг, указывающий, что сведения об устаревшей сущности готовы к добавлению в обновлённые. + /// public bool LegacyPrepared { get; private set; } = false; - public List RemasterEntities = []; + /// + /// Список обновлённых сущностей, что отметели устаревшую сущность как предшественника. + /// + public List RemasterEntities { get; } = []; + /// + /// Конструктор класса , если сущность сама отмечена как устаревшая. + /// + /// Обработчик устаревшей сущности. public LegacyEntityHandler(AbstractEntityProcessor processor) { LegacyId = processor.Input.GetRequired("id"); Prepare(processor); } + /// + /// Конструктор класса , если обновлённая сущность указывает на ещё недобавленную устаревшую сущность. + /// + /// Идентификатор устаревшей сущности. + /// Обновлённая сущность. public LegacyEntityHandler(string legacyId, Entity remasterEntity) { LegacyId = legacyId; @@ -31,6 +57,10 @@ internal class LegacyEntityHandler RemasterEntities.Add(remasterEntity); } + /// + /// Подготовить данные об устравшей сущности. + /// + /// Обработчик устаревшей сущности. public void Prepare(AbstractEntityProcessor processor) { if (LegacyPrepared) @@ -48,6 +78,10 @@ internal class LegacyEntityHandler } } + /// + /// Применить данные устаревшей сущности к обновлённой. + /// + /// Обновлённая сущность. public void Apply(Entity remasterEntity) { if (!remasterEntity.Name.Contains(LegacyName)) diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/AbstractEntityProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/AbstractEntityProcessor.cs index 828cd3a..7876221 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/AbstractEntityProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/AbstractEntityProcessor.cs @@ -14,17 +14,37 @@ using Pf2eModel.Model.Structure; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors; +/// +/// Абстрактный класс-обработчик сущностей, полученных из Архивов Нефиса. +/// Класс предназначен для обработки данных с помощью различных обработчиков, +/// для которых указан атрибут . +/// +/// Объект обработки вводимого JSON. +/// 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("remaster_id") is not null) @@ -42,27 +62,54 @@ internal abstract class AbstractEntityProcessor(AonprdProcessorInput input) ProcessLegacy(); } + /// public override void Save() { Add(); Context.SaveChanges(); } - // Description generation + #region "Генерация описания" - protected virtual string[] AddedDescriptionPaths() => []; + /// + /// Получение списка полей, которые должны быть добавлены в описание. + /// + /// + // [Obsolete("Данный метод будет отключён на финальных этапах разработки.")] + protected virtual ICollection AddedDescriptionPaths() => []; + /// + /// Создание строкового представления объекта по указанному пути для добавления к описанию сущности. + /// + /// Путь к добавляемому объекту. + /// Строковое представление для добавления к описанию сущности. + // [Obsolete("Данный метод будет отключён на финальных этапах разработки.")] protected string AddedDescription(string path) => - $"\r\n\r\n========== [{path}] ==========\r\n\r\n{Input.GetOptional(path) ?? ""}"; + $"\r\n\r\n========== [{path}] ==========\r\n\r\n{Input.GetOptional(path)?.ToString() ?? ""}"; - protected string AddedDescription(string[] paths) => + /// + /// Создание строкового представления объектов по указанным путям для добавления к описанию сущности. + /// + /// Список путей к добавляемым объектам. + /// Строковое представление для добавления к описанию сущности. + // [Obsolete("Данный метод будет отключён на финальных этапах разработки.")] + protected string AddedDescription(ICollection paths) => string.Join("", paths.Select(AddedDescription)); + /// + /// Сгенерировать описание сущности. + /// + /// Описание сущности protected virtual string GetDescription() => Input.GetRequired("markdown") + AddedDescription(AddedDescriptionPaths()); - // Entity generation + #endregion + #region "Генерация сущности" + + /// + /// Генерация основных полей корневой сущности. + /// protected virtual void AddEntity() { Entity.Id = Helper.NextEntityId; @@ -81,6 +128,9 @@ internal abstract class AbstractEntityProcessor(AonprdProcessorInput input) Context.Entities.Add(Entity); } + /// + /// Генерация источников сущности. + /// protected virtual void AddSource() { foreach (EntitySource entitySource in GetSources()) @@ -89,6 +139,9 @@ internal abstract class AbstractEntityProcessor(AonprdProcessorInput input) } } + /// + /// Генерация внешних ссылок сущности. + /// protected virtual void AddExternal() { EntityExternal external = new() @@ -102,6 +155,10 @@ internal abstract class AbstractEntityProcessor(AonprdProcessorInput input) Context.EntityExternals.Add(external); } + /// + /// Генерация списка признаков сущности. + /// + /// protected virtual void AddTraits() { JToken? traitsJson = Input.Input.SelectToken("trait"); @@ -137,6 +194,13 @@ internal abstract class AbstractEntityProcessor(AonprdProcessorInput input) } } + #endregion + + /// + /// Обработка сущности, отмеченной как устаревшей. + /// + /// Обработка происходит в классе + /// protected virtual void ProcessLegacy() { JArray? legacyIdArray = Input.GetOptional("legacy_id"); @@ -171,6 +235,10 @@ internal abstract class AbstractEntityProcessor(AonprdProcessorInput input) } } + /// + /// Получение списка источников сущности. + /// + /// public List GetSources() { string[] sourceNames = Input @@ -186,7 +254,7 @@ internal abstract class AbstractEntityProcessor(AonprdProcessorInput input) ); } - List entitySources = new(); + List entitySources = []; foreach (string sourceName in sourceNames) { @@ -208,11 +276,16 @@ internal abstract class AbstractEntityProcessor(AonprdProcessorInput input) return entitySources; } - public string GenerateExtra(params string[] args) + /// + /// Генерация JSON-строки из объектов, располагаюхися по путям, указанным в . + /// + /// Коллекция путей, из которых необходимо достатить объекты. + /// JSON-строка. + public string GenerateJsonString(params ICollection paths) { Dictionary data = []; - foreach (string key in args) + foreach (string key in paths) { data.Add(key, Input.Input.SelectToken(key)); } @@ -220,6 +293,12 @@ internal abstract class AbstractEntityProcessor(AonprdProcessorInput input) return JsonConvert.SerializeObject(data, new JsonSerializerSettings()); } + /// + /// Создание контрольной суммы объекта. + /// + /// Строковое представление вычисляемого объекта. + /// Использовать шестнадцатеричные цифры в нижнем регистре. + /// Контрольная сумма объекта. public static string Md5(string input, bool isLowercase = false) { using MD5 md5 = MD5.Create(); @@ -231,8 +310,17 @@ internal abstract class AbstractEntityProcessor(AonprdProcessorInput input) protected static readonly List LegacyHandlers = []; } +/// +/// Исключение, показывающее, что обработка сущности пропускается как устаревшая. +/// +/// Название сущности. 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}.") { } diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/AbstractProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/AbstractProcessor.cs index 1f35a70..2129587 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/AbstractProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/AbstractProcessor.cs @@ -3,19 +3,47 @@ using Pf2eModel; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors; +/// +/// Абстрактный класс-обработчик данных, полученных из Архивов Нефиса. +/// Класс предназначен для обработки данных с помощью различных обработчиков, +/// для которых указан атрибут . +/// +/// Объект обработки вводимого JSON. +/// internal abstract class AbstractProcessor(AonprdProcessorInput input) { + /// + /// Вспомогательный класс для работы с базой данных. + /// protected readonly DatabaseHelper Helper = DatabaseHelper.Instance; + /// + /// Вспомогательный класс для работы со строками и форматированием. + /// protected readonly StringHelper StringHelper = StringHelper.Instance; + /// + /// Вспомогательный класс для работы с глобальными настройками. + /// protected readonly AonprdBuilderGlobals Globals = AonprdBuilderGlobals.Instance; + /// + /// Контекст базы данных для работы с данными. + /// public readonly Pf2eDbContext Context = DatabaseHelper.DbContext; + /// + /// Объект обработки вводимого JSON. + /// public readonly AonprdProcessorInput Input = input; + /// + /// Основной метод обработки данных. + /// public abstract void Process(); + /// + /// Метод для сохранения изменений в базе данных. + /// public abstract void Save(); } diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/AbstractSubclassProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/AbstractSubclassProcessor.cs index 11f52fd..df85831 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/AbstractSubclassProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/AbstractSubclassProcessor.cs @@ -6,32 +6,56 @@ using Pf2eModel.Model.Structure; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors; +/// +/// Абстрактный класс-обработчик сущностей подклассов, полученных из Архивов Нефиса. +/// Класс предназначен для обработки данных с помощью различных обработчиков, +/// для которых указан атрибут . +/// +/// Объект обработки вводимого JSON. +/// internal abstract class AbstractSubclassProcessor(AonprdProcessorInput input) : AbstractEntityProcessor(input) { + /// public override EntityType Type => EntityType.Subclass; + /// public abstract override Subclass Object { get; } + /// + /// Название подкласса. + /// public abstract string? ClassName { get; } + /// + /// Название группы подклассов. + /// public abstract string GroupName { get; } + /// public override void Add() => Context.Subclasses.Add(Object); + /// public override void Process() { base.Process(); Object.Group = GetGroup(); + ProcessObject(); } + /// + /// Обработка полей подкласса. + /// public abstract void ProcessObject(); + /// + /// Получение CLR-представления группы подклассов, к которому относится обрабатываемый подкласс. + /// + /// CLR-представления группы подклассов. public SubclassGroup GetGroup() { SubclassGroup? group = Context.SubclassGroups.FirstOrDefault(g => - // ReSharper disable once EntityFramework.UnsupportedServerSideFunctionCall Pf2eDbContext.LocaleSearch(g.Entity.Name, GroupName) ); @@ -42,7 +66,6 @@ internal abstract class AbstractSubclassProcessor(AonprdProcessorInput input) Class? cl = ClassName is not null ? Context.Classes.FirstOrDefault(c => - // ReSharper disable once EntityFramework.UnsupportedServerSideFunctionCall Pf2eDbContext.LocaleSearch(c.Entity.Name, GroupName) ) : null; diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Common/ExternalProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Common/ExternalProcessor.cs index a5cdc90..fd688fc 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Common/ExternalProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Common/ExternalProcessor.cs @@ -11,7 +11,7 @@ internal class ExternalProcessor(AonprdProcessorInput input) : AbstractProcessor { AonprdTarget = new() { - Name = AonprdImporter.SourceName, + Name = AonprdImporter.TargetName, Icon = "http://www.google.com/s2/favicons?domain=2e.aonprd.com", Prefix = "https://2e.aonprd.com", }; diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Common/SkillProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Common/SkillProcessor.cs index fd67102..26ab2ed 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Common/SkillProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Common/SkillProcessor.cs @@ -7,8 +7,15 @@ namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processo [AonprdProcessor("skill", [], false)] internal class SkillProcessor(AonprdProcessorInput input) : AbstractProcessor(input) { + /// + /// Обучаемый объект. + /// public Training Training = null!; + /// + public override void Save() => Context.Trainings.Add(Training); + + /// public override void Process() { Training = new() @@ -17,9 +24,4 @@ internal class SkillProcessor(AonprdProcessorInput input) : AbstractProcessor(in Type = TrainingType.Skill, }; } - - public override void Save() - { - Context.Trainings.Add(Training); - } } diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Common/SourceProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Common/SourceProcessor.cs index 9d25697..8f0a417 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Common/SourceProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Common/SourceProcessor.cs @@ -6,8 +6,12 @@ namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processo [AonprdProcessor("source", [], false)] internal class SourceProcessor(AonprdProcessorInput input) : AbstractProcessor(input) { + /// + /// Источник сущности. + /// protected Source Source = null!; + /// public override void Process() { Source = new() @@ -18,6 +22,7 @@ internal class SourceProcessor(AonprdProcessorInput input) : AbstractProcessor(i }; } + /// public override void Save() { Context.Sources.Add(Source); diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Subclasses/ApparitionProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Subclasses/ApparitionProcessor.cs index 0762fa2..b89bf55 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Subclasses/ApparitionProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Subclasses/ApparitionProcessor.cs @@ -1,6 +1,4 @@ -using Microsoft.EntityFrameworkCore; -using Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Helpers; -using Pf2eModel; +using Pf2eModel; using Pf2eModel.Model.Base; using Pf2eModel.Model.Common; using Pf2eModel.Model.Common.I18n; @@ -12,12 +10,16 @@ namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processo [AonprdProcessor("apparition", ["class", "spell", "skill"])] internal class ApparitionProcessor(AonprdProcessorInput input) : AbstractSubclassProcessor(input) { + /// public override Subclass Object { get; } = new(); + /// public override string ClassName => "Animist"; + /// public override string GroupName => "Apparition"; + /// public override void ProcessObject() { // Spells @@ -52,7 +54,7 @@ internal class ApparitionProcessor(AonprdProcessorInput input) : AbstractSubclas foreach (string name in trainingNames) { - Training? training = trainings.FirstOrDefault(t => t.Name!.Contains(name)); + Training? training = trainings.FirstOrDefault(t => t.Name.Contains(name)); training ??= new() { Name = new() { { LocaleType.English, name } }, @@ -70,6 +72,7 @@ internal class ApparitionProcessor(AonprdProcessorInput input) : AbstractSubclas } } + /// protected override string GetDescription() => base.GetDescription() + AddedDescription(["skill_markdown", "spell_markdown", "summary_markdown"]); diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/ActionProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/ActionProcessor.cs index 6a83c06..8fd6b55 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/ActionProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/ActionProcessor.cs @@ -5,12 +5,16 @@ namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processo [AonprdProcessor("action")] internal class ActionProcessor(AonprdProcessorInput input) : AbstractEntityProcessor(input) { + /// public override EntityType Type => EntityType.Action; + /// public override Pf2eModel.Model.Structure.Action Object { get; } = new(); + /// public override void Add() => Context.Actions.Add(Object); + /// public override void Process() { if (Input.GetRequired("markdown").StartsWith("**Activate**")) @@ -21,20 +25,18 @@ internal class ActionProcessor(AonprdProcessorInput input) : AbstractEntityProce base.Process(); Object.Actions = Input.EnumOptional("actions", ActionEnum.Passive); - Object.Extra = GenerateExtra("school"); + Object.Extra = GenerateJsonString("school"); } - protected override string GetDescription() => - base.GetDescription() - + AddedDescription( - [ - "cost_markdown", - "trait_markdown", - "source_markdown", - "search_markdown", - "requirement_markdown", - "trigger_markdown", - "summary_markdown", - ] - ); + /// + protected override string[] AddedDescriptionPaths() => + [ + "cost_markdown", + "trait_markdown", + "source_markdown", + "search_markdown", + "requirement_markdown", + "trigger_markdown", + "summary_markdown", + ]; } diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/AncestryProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/AncestryProcessor.cs index 8f9d2a3..d0f16b0 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/AncestryProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/AncestryProcessor.cs @@ -7,12 +7,16 @@ namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processo [AonprdProcessor("ancestry", ["trait", "source", "language"])] internal class AncestryProcessor(AonprdProcessorInput input) : AbstractEntityProcessor(input) { + /// public override EntityType Type => EntityType.Ancestry; + /// public override Ancestry Object { get; } = new(); + /// public override void Add() => Context.Ancestries.Add(Object); + /// public override void Process() { string type = Input.GetRequired("type"); diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/ArchetypeProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/ArchetypeProcessor.cs index a514d97..a4f5c0b 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/ArchetypeProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/ArchetypeProcessor.cs @@ -6,12 +6,16 @@ namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processo [AonprdProcessor("archetype")] internal class ArchetypeProcessor(AonprdProcessorInput input) : AbstractEntityProcessor(input) { + /// public override EntityType Type => EntityType.Archetype; + /// public override Archetype Object { get; } = new(); + /// public override void Add() => Context.Archetypes.Add(Object); + /// public override void Process() { base.Process(); @@ -19,7 +23,7 @@ internal class ArchetypeProcessor(AonprdProcessorInput input) : AbstractEntityPr Object.Level = Input.GetOptional("level"); } - protected override string GetDescription() => - base.GetDescription() - + AddedDescription(["prerequisite_markdown", "requirement_markdown", "summary_markdown"]); + /// + protected override string[] AddedDescriptionPaths() => + ["prerequisite_markdown", "requirement_markdown", "summary_markdown"]; } diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/BackgroundProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/BackgroundProcessor.cs index 7bf29a4..cc1b8e2 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/BackgroundProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/BackgroundProcessor.cs @@ -8,12 +8,16 @@ namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processo [AonprdProcessor("background")] internal class BackgroundProcessor(AonprdProcessorInput input) : AbstractEntityProcessor(input) { + /// public override EntityType Type => EntityType.Background; + /// public override Background Object { get; } = new(); + /// public override void Add() => Context.Backgrounds.Add(Object); + /// public override void Process() { base.Process(); @@ -31,9 +35,7 @@ internal class BackgroundProcessor(AonprdProcessorInput input) : AbstractEntityP } } - protected override string GetDescription() => - base.GetDescription() - + AddedDescription( - ["feat_markdown", "summary_markdown", "skill_markdown", "prerequisite_markdown"] - ); + /// + protected override string[] AddedDescriptionPaths() => + ["feat_markdown", "summary_markdown", "skill_markdown", "prerequisite_markdown"]; } diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/ClassProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/ClassProcessor.cs index 7c60ec7..c70d24d 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/ClassProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/ClassProcessor.cs @@ -6,18 +6,23 @@ using Pf2eModel.Model.Common.I18n; using Pf2eModel.Model.Enum; using Pf2eModel.Model.Structure; using Pf2eModel.Model.Structure.ClassRelation; +using static System.Text.RegularExpressions.Regex; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types; [AonprdProcessor("class", ["trait", "source", "skill"])] -internal class ClassProcessor(AonprdProcessorInput input) : AbstractEntityProcessor(input) +internal partial class ClassProcessor(AonprdProcessorInput input) : AbstractEntityProcessor(input) { + /// public override EntityType Type => EntityType.Class; + /// public override Class Object { get; } = new(); + /// public override void Add() => Context.Classes.Add(Object); + /// public override void Process() { base.Process(); @@ -79,9 +84,13 @@ internal class ClassProcessor(AonprdProcessorInput input) : AbstractEntityProces break; } - ProcessDependecies(); + ProcessClassTrait(); } + /// + /// Получение коллекции обучений, связанных с классом. + /// + /// Коллекция обучения. protected ICollection GetSkillTrainings() { List skillNames = Input @@ -103,6 +112,12 @@ internal class ClassProcessor(AonprdProcessorInput input) : AbstractEntityProces .ToArray(); } + /// + /// Обработка массива со списком обучений. + /// + /// JSON-массив обучений. + /// Тип обучения. + /// Коллекция обучений. protected ICollection ProcessTraining(JArray array, TrainingType type) { const string pattern = @"^(.*?) in\s+(?:the)?\s*(.*)$"; @@ -110,18 +125,16 @@ internal class ClassProcessor(AonprdProcessorInput input) : AbstractEntityProces foreach (string value in array.Select(token => token.ToString())) { - MatchCollection m = Regex.Matches(value, pattern); + MatchCollection m = Matches(value, pattern); if (m.Count == 0) { - throw new Exception($"Can't parse training string '{value}'"); + throw new($"Can't parse training string '{value}'"); } TrainingRank rank = Input.Enum(m[0].Groups[1].Value); - string[] parts = GetTrainingString(m[0].Groups[2].Value); - - foreach (string part in parts) + foreach (string part in GetTrainingString(m[0].Groups[2].Value)) { Training? training = Context.Trainings.FirstOrDefault(t => Pf2eDbContext.LocaleSearch(t.Name, part) && t.Type == type @@ -129,7 +142,7 @@ internal class ClassProcessor(AonprdProcessorInput input) : AbstractEntityProces if (training == null) { - training = new Training() + training = new() { Name = new() { [LocaleType.English] = part }, Type = type, @@ -152,17 +165,26 @@ internal class ClassProcessor(AonprdProcessorInput input) : AbstractEntityProces return trainings; } - protected string[] GetTrainingString(string value) + /// + /// Получение коллекции названий обучений из строкового представления из Архивов Нефиса. + /// + /// Строковое представление обучений из Архивов Нефиса. + /// Коллекция названий обучений. + protected ICollection GetTrainingString(string value) { return value.StartsWith("favored weapon of your deity") ? ["Favored weapon of your deity"] : value == "simple and martial weapons" ? ["Simple weapon", "Martial weapon"] - : Regex - .Split(value, @"\s*(?:,|and)\s*") + : TrainingStringSeparatorRegex() + .Split(value) .Where(s => s != "") .Select(s => string.Concat(s[0].ToString().ToUpper(), s.AsSpan(1))) .ToArray(); } + /// + /// Получение CLR-представления обучений спасброскам. + /// + /// Коллекция обучений. protected ICollection GetSaveTrainings() { Dictionary result = Context @@ -201,7 +223,10 @@ internal class ClassProcessor(AonprdProcessorInput input) : AbstractEntityProces .ToList(); } - protected void ProcessDependecies() + /// + /// Объявление соотеветсвующего классу признака классовым. + /// + protected void ProcessClassTrait() { Trait? classTrait = Context.Traits.FirstOrDefault(t => t.Entity.Name == Entity.Name); @@ -212,4 +237,11 @@ internal class ClassProcessor(AonprdProcessorInput input) : AbstractEntityProces classTrait.Type = TraitType.Class; } + + /// + /// Получение регулярного выражения для разделения строки строкового представления обучений. + /// + /// Регулярное выражение. + [GeneratedRegex(@"\s*(?:,|and)\s*")] + protected static partial Regex TrainingStringSeparatorRegex(); } diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/FeatProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/FeatProcessor.cs index c818911..aa93139 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/FeatProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/FeatProcessor.cs @@ -8,12 +8,16 @@ namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processo [AonprdProcessor("feat", ["trait", "source", "archetype"])] internal class FeatProcessor(AonprdProcessorInput input) : AbstractEntityProcessor(input) { + /// public override EntityType Type => EntityType.Feat; + /// public override Feat Object { get; } = new(); + /// public override void Add() => Context.Feats.Add(Object); + /// public override void Process() { base.Process(); @@ -33,7 +37,7 @@ internal class FeatProcessor(AonprdProcessorInput input) : AbstractEntityProcess Object.IsPassive = action.IsPassive; Object.IsReaction = action.IsReaction; - Object.Extra = GenerateExtra( + Object.Extra = GenerateJsonString( "archetype", "element", "heighten", @@ -68,6 +72,7 @@ internal class FeatProcessor(AonprdProcessorInput input) : AbstractEntityProcess } } + /// protected override string GetDescription() => base.GetDescription() + AddedDescription( diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/LanguageProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/LanguageProcessor.cs index 10599e7..206746f 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/LanguageProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/LanguageProcessor.cs @@ -6,9 +6,12 @@ namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processo [AonprdProcessor("language")] internal class LanguageProcessor(AonprdProcessorInput input) : AbstractEntityProcessor(input) { + /// public override EntityType Type => EntityType.Language; + /// public override Language Object { get; } = new(); + /// public override void Add() => Context.Languages.Add(Object); } diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/RitualProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/RitualProcessor.cs index f1d8bc1..b4e7430 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/RitualProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/RitualProcessor.cs @@ -1,30 +1,27 @@ -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Text.RegularExpressions; -using System.Xml.Linq; -using Microsoft.EntityFrameworkCore; +using System.Text.RegularExpressions; using Newtonsoft.Json.Linq; using Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Helpers; using Pf2eModel; -using Pf2eModel.Model.Common; using Pf2eModel.Model.Common.I18n; using Pf2eModel.Model.Enum; using Pf2eModel.Model.Structure; using Pf2eModel.Model.Structure.RitualRelation; -using Pf2eModel.Model.Structure.SpellRelation; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types; [AonprdProcessor("ritual", ["trait", "source", "skill"])] internal partial class RitualProcessor(AonprdProcessorInput input) : AbstractEntityProcessor(input) { + /// public override EntityType Type => EntityType.Ritual; + /// public override Ritual Object { get; } = new(); + /// public override void Add() => Context.Rituals.Add(Object); + /// public override void Process() { base.Process(); @@ -365,6 +362,7 @@ internal partial class RitualProcessor(AonprdProcessorInput input) : AbstractEnt return result; } + /// protected override string[] AddedDescriptionPaths() => [ "summary_markdown", diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/Rules/RuleProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/Rules/RuleProcessor.cs index dd64ae3..43cbd04 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/Rules/RuleProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/Rules/RuleProcessor.cs @@ -1,11 +1,8 @@ -using System.Data; -using Microsoft.EntityFrameworkCore.Migrations.Operations; -using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Linq; using Pf2eModel.Model.Base.Source; using Pf2eModel.Model.Base.Targets; 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.Rules; @@ -13,14 +10,21 @@ namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processo [AonprdProcessor("rules", ["rules-toc"])] 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 readonly RuleTocCollection Toc = RuleTocCollection.Instance; + /// + /// Структура генерации содержания правил. + /// + public RuleTocCollection Toc { get; } = RuleTocCollection.Instance; + /// public override void Process() { // base.Process(); @@ -93,6 +97,10 @@ internal class RuleProcessor(AonprdProcessorInput input) : AbstractEntityProcess */ } + /// + /// Получение источника, к которому относится правило. + /// + /// Источник, к которому относится правило. private Source GetSource() { foreach (EntitySource entitySource in Entity.Sources) @@ -103,6 +111,10 @@ internal class RuleProcessor(AonprdProcessorInput input) : AbstractEntityProcess throw new("Source not found"); } + /// + /// Получение внешней ссылки, из которой получено правило. + /// + /// Внешняя ссылка. private EntityExternal GetExternal() { foreach (EntityExternal entityExternal in Entity.Externals) diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/Rules/RuleToc.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/Rules/RuleToc.cs index 2db21cc..7d59e6c 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/Rules/RuleToc.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/Rules/RuleToc.cs @@ -1,17 +1,27 @@ -using System.Linq; -using System.Runtime.CompilerServices; -using LinqToDB.DataProvider.ClickHouse; -using Pf2eModel.Model.Base.Source; +using Pf2eModel.Model.Base.Source; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types.Rules; +/// +/// Класс коллекции книг, содержащих правила. +/// Реализует шаблон Singleton. +/// internal class RuleTocCollection { + /// + /// Список книг, содержащих правила. + /// public List Books { get; } = []; + /// + /// Поиск книги по числовому идентификатору. + /// public RuleTocBook this[int id] => Books.FirstOrDefault(i => i.SourceId == id) ?? throw new($"Book with id {id} not found"); + /// + /// Поиск книги по CLR-представлению источника. + /// public RuleTocBook this[Source source] { get @@ -29,6 +39,9 @@ internal class RuleTocCollection } } + /// + /// Поиск книги по имени. + /// public RuleTocBook? FindName(string name) { foreach (RuleTocBook book in Books) @@ -42,6 +55,9 @@ internal class RuleTocCollection return null; } + /// + /// Поиск правила по идентификатору. + /// public RuleTocItem? FindEntityId(int id) { foreach (RuleTocBook book in Books) @@ -57,13 +73,17 @@ internal class RuleTocCollection return null; } - public static RuleTocCollection Instance { get; } = new(); - + /// + /// Возвращает строковое представление коллекции в файле YAML. + /// public override string ToString() { return string.Join("\n", Books.Select(book => book.ToString()).ToList()); } + /// + /// Сохраняет коллекцию в файле YAML. + /// public void SaveFile() { string fileName = @@ -74,15 +94,41 @@ internal class RuleTocCollection writer.Write(ToString()); } + + /// + /// Экземпляр класса . + /// + public static RuleTocCollection Instance { get; } = new(); } +/// +/// Класс представляет собой книгу, содержащую правила. +/// internal class RuleTocBook(Source source) { + /// + /// Идентификатор источника. + /// public int SourceId { get; } = source.Id; + + /// + /// Названия источника. + /// public string SourceName { get; } = source.Name; + /// + /// Список дочерних правил. + /// + /// + /// Список дочерних элементов. + /// public List Children { get; } = []; + /// + /// Находит элемент по хлебным крошкам. + /// + /// Массив хлебных крошек, указывающих на путь в дереве. + /// или null если элемент не найден. public RuleTocItem Get(string[] breadcrumbs) { RuleTocItem? node = null; @@ -110,6 +156,12 @@ internal class RuleTocBook(Source source) } } + /// + /// Проверяет, существует ли имя в текущей книге или дочерних элементах. + /// + /// Имя для проверки. + /// Начальный элемент для поиска. + /// true если имя. public bool IsNameExists(string name, RuleTocItem? node = null) { foreach (RuleTocItem item in node?.Children ?? Children) @@ -123,6 +175,12 @@ internal class RuleTocBook(Source source) return false; } + /// + /// Находит элемент по идентификатору сущности. + /// + /// Идентификатор сущности. + /// Начальный узел для поиска. + /// или null если элемент не найден. public RuleTocItem? GetByEntityId(int id, RuleTocItem? node = null) { if (node?.RuleId == id) @@ -143,6 +201,10 @@ internal class RuleTocBook(Source source) return null; } + /// + /// Возвращает строковое представление книги в файле YAML. + /// + /// Строковое представление книги. public override string ToString() { return $"\"{SourceName}\":\n" @@ -153,28 +215,67 @@ internal class RuleTocBook(Source source) } } +/// +/// Класс представляет собой элемент дерева правил. +/// internal class RuleTocItem(string name, RuleTocItem? parent) { public string? Id { get; set; } = null; + /// + /// Имя элемента. + /// public string Name { get; } = name; + /// + /// Идентификатор правила. + /// public int? RuleId { get; set; } = null; + /// + /// Указывает, что элемент обрабатывается. + /// public bool IsHandled { get; set; } = false; + /// + /// Родительский элемент дерева. + /// public RuleTocItem? Parent { get; } = parent; + /// + /// Порядок дочерних элементов. + /// public List? ChildrenOrder { get; set; } = null; + /// + /// Коллекция дочерних элементов. + /// public List Children { get; } = []; + /// + /// Предыдущий идентификатор. + /// public string? PreviousId { get; set; } + + /// + /// Следующий идентификатор. + /// public string? NextId { get; set; } + /// + /// Идентификатор первого дочернего элемента. + /// public string? FirstChildId { get; set; } + + /// + /// Идентификатор последнего дочернего элемента. + /// public string? LastChildId { get; set; } + /// + /// Возвращает строковое представление элемента содержания в файле YAML. + /// + /// Строковое представление элемента содержания. public override string ToString() { List rows = @@ -200,6 +301,12 @@ internal class RuleTocItem(string name, RuleTocItem? parent) return string.Join("\n", rows); } + /// + /// Увеличение отступа строкового представления элемента содержания в файле YAML. + /// + /// Количество отступов. + /// Строковое представление элемента содержания с отступом. + /// public string Indent(int amount = 1) { string result = ToString(); @@ -210,10 +317,21 @@ internal class RuleTocItem(string name, RuleTocItem? parent) return result; } + /// + /// Увеличение отступа строки. + /// + /// Входная строка. + /// Строка с отступом. + /// public static string Indent(string str) { return Tab + str.Replace("\n", $"\n{Tab}"); } + /// + /// Строка отступа строки. + /// + /// + /// public const string Tab = " "; } diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/Rules/RulesOrderProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/Rules/RulesOrderProcessor.cs index 11d7e53..98c23d3 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/Rules/RulesOrderProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/Rules/RulesOrderProcessor.cs @@ -14,10 +14,17 @@ namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processo [AonprdProcessor("rules-order", null, ["rules"], false)] internal class RulesOrderProcessor(AonprdProcessorInput input) : AbstractProcessor(input) { + /// + /// Структура генерации содержания правил. + /// public readonly RuleTocCollection Toc = RuleTocCollection.Instance; - private readonly List OrderItems = []; + /// + /// Список записей о порядке правил в своих главах. + /// + private readonly List _orderItems = []; + /// public override void Process() { Toc.SaveFile(); @@ -38,6 +45,10 @@ internal class RulesOrderProcessor(AonprdProcessorInput input) : AbstractProcess } } + /// + /// Обработка конкретного правила. + /// + /// private void Process(Rule rule) { RuleTocItem? item = Toc.FindEntityId(rule.Id); @@ -52,6 +63,10 @@ internal class RulesOrderProcessor(AonprdProcessorInput input) : AbstractProcess // Дети будут раставлены автоматически по связи } + /// + /// Построение списка записей о порядке правил из дочерних элементов содержания. + /// + /// Элемент содержания, дочерние элементы которого необходимо обработать. private void BuildIdOrderDictionary(RuleTocItem item) { List items = []; @@ -88,6 +103,10 @@ internal class RulesOrderProcessor(AonprdProcessorInput input) : AbstractProcess BuildIdOrderDictionary(items); } + /// + /// Построение списка записей о порядке правил из списка элементов содержания. + /// + /// Список элементов содержания для обработки. private void BuildIdOrderDictionary(List items) { for (int i = 0; i < items.Count; i++) @@ -104,14 +123,20 @@ internal class RulesOrderProcessor(AonprdProcessorInput input) : AbstractProcess } } + /// + /// Добавление элемента содержания. + /// + /// Идентификатор правила. + /// Порядок правила. + /// Идентификатор главы, в которой находится глава. private void AddOrderItem(int id, int order, int? parentId) { - if (OrderItems.Any(item => item.Id == id)) + if (_orderItems.Any(item => item.Id == id)) { throw new($"Order item with id {id} already exists"); } - OrderItems.Add( + _orderItems.Add( new() { Id = id, @@ -121,15 +146,30 @@ internal class RulesOrderProcessor(AonprdProcessorInput input) : AbstractProcess ); } + /// public override void Save() { Context.SaveChanges(); } + /// + /// Запись, указывающая порядок правила в своей главе. + /// private record OrderItem { + /// + /// Идентификатор правила. + /// public int Id { get; init; } + + /// + /// Порядок правила. + /// public int Order { get; init; } + + /// + /// Идентификатор главы, в которой находится глава. + /// public int? ParentId { get; init; } } } diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/Rules/RulesTocProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/Rules/RulesTocProcessor.cs index bf202f3..511d4a4 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/Rules/RulesTocProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/Rules/RulesTocProcessor.cs @@ -9,11 +9,22 @@ namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processo [AonprdProcessor("rules-toc", null, ["source"], false)] internal partial class RulesTocProcessor(AonprdProcessorInput input) : AbstractProcessor(input) { + /// + /// Селектор HTML-элемента, в котором находится содержание. + /// private const string Selector = "#ctl00_RadDrawer1_Content_MainContent_DetailedOutput"; + + /// + /// Ссылка на страницу с содержанием. + /// private const string Url = "https://2e.aonprd.com/Rules.aspx"; + /// + /// Структура генерации содержания правил. + /// public readonly RuleTocCollection Toc = RuleTocCollection.Instance; + /// public override void Process() { IDocument document = DownloadPage(); @@ -64,6 +75,12 @@ internal partial class RulesTocProcessor(AonprdProcessorInput input) : AbstractP Toc.SaveFile(); } + /// + /// Обработка ссылки в содержании для создания нового элемента содержания.. + /// + /// Строка с ссылкой. + /// Родительсий элемент, к которому будет относиться новый элемент содержания. + /// Новый элемент содержания. private RuleTocItem ParseLink(string value, RuleTocItem? parent = null) { Match linkMatch = LinkRegex().Match(value); @@ -79,6 +96,10 @@ internal partial class RulesTocProcessor(AonprdProcessorInput input) : AbstractP }; } + /// + /// Скачивание страницы, указанной в , и получение очищенного элемента, указанного в . + /// + /// private IDocument DownloadPage() { IConfiguration config = Configuration.Default.WithDefaultLoader(); @@ -89,7 +110,7 @@ internal partial class RulesTocProcessor(AonprdProcessorInput input) : AbstractP string html = root.OuterHtml; - foreach (string s in new string[] { "br", "\r", "\n", "", "", "&NoRedirect=1" }) + foreach (string s in new[] { "br", "\r", "\n", "", "", "&NoRedirect=1" }) { html = html.Replace(s, ""); } @@ -97,8 +118,13 @@ internal partial class RulesTocProcessor(AonprdProcessorInput input) : AbstractP return context.OpenAsync(req => req.Content(html)).Result; } + /// public override void Save() { } - [GeneratedRegex(@"(.*?)")] + /// + /// Получить скомпилированное регулярное выражение для получения HTML-ссылки. + /// + /// Регулярное выражение получения HTML-ссылки. + [GeneratedRegex("""(.*?)""")] public static partial Regex LinkRegex(); } diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/SpellProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/SpellProcessor.cs index 0a22e12..b1c4b71 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/SpellProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/SpellProcessor.cs @@ -9,12 +9,16 @@ namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processo [AonprdProcessor("spell")] internal class SpellProcessor(AonprdProcessorInput input) : AbstractEntityProcessor(input) { + /// public override EntityType Type => EntityType.Spell; + /// public override Spell Object { get; } = new(); + /// public override void Add() => Context.Spells.Add(Object); + /// public override void Process() { base.Process(); @@ -51,6 +55,9 @@ internal class SpellProcessor(AonprdProcessorInput input) : AbstractEntityProces ProcessArea(); } + /// + /// Обработка области применения заклинания. + /// private void ProcessArea() { List areaTypes = (Input.GetOptional("area_type") ?? []).ToList(); @@ -112,23 +119,21 @@ internal class SpellProcessor(AonprdProcessorInput input) : AbstractEntityProces } } - protected override string GetDescription() => - base.GetDescription() - + AddedDescription( - [ - "range", - "range_raw", - "bloodline_markdown", - "cost_markdown", - "deity_markdown", - "domain_markdown", - "lesson_markdown", - "mystery_markdown", - "requirement_markdown", - "summary_markdown", - "tradition_markdown", - "patron_theme_markdown", - "saving_throw_markdown", - ] - ); + /// + protected override string[] AddedDescriptionPaths() => + [ + "range", + "range_raw", + "bloodline_markdown", + "cost_markdown", + "deity_markdown", + "domain_markdown", + "lesson_markdown", + "mystery_markdown", + "requirement_markdown", + "summary_markdown", + "tradition_markdown", + "patron_theme_markdown", + "saving_throw_markdown", + ]; } diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/TraitProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/TraitProcessor.cs index 49c6ac6..a2d729f 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/TraitProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/TraitProcessor.cs @@ -6,12 +6,16 @@ namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processo [AonprdProcessor("trait", ["external"], false)] internal class TraitProcessor(AonprdProcessorInput input) : AbstractEntityProcessor(input) { + /// public override EntityType Type => EntityType.Trait; + /// public override Trait Object { get; } = new(); + /// public override void Add() => Context.Traits.Add(Object); + /// public override void Process() { base.Process(); @@ -21,11 +25,13 @@ internal class TraitProcessor(AonprdProcessorInput input) : AbstractEntityProces Context.Traits.Add(Object); } + /// protected override void AddTraits() { // Необходимо пропускать } + /// protected override string GetDescription() => base.GetDescription() + AddedDescription(["summary_markdown", "search_markdown"]); } diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/SchemaBuilder/AonprdSchemaBuilder.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/SchemaBuilder/AonprdSchemaBuilder.cs index 713c5d7..3551a72 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/SchemaBuilder/AonprdSchemaBuilder.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/SchemaBuilder/AonprdSchemaBuilder.cs @@ -4,20 +4,34 @@ using Pf2eDataScrapper.SchemaBuilder; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.SchemaBuilder; +/// +/// Класс создания схемы данных с сайта Архивов Нефеса. +/// +/// Загузчик данных с сайта Архивов Нефеса. internal class AonprdSchemaBuilder(AonprdLoader loader) { + /// + /// Загузчик данных с сайта Архивов Нефеса. + /// public readonly AonprdLoader Loader = loader; + /// public readonly AonprdCompendium Compendium = loader.Compendium; // public readonly AonprdSchemaTypeResolver Resolver = new(); + /// + /// Дополнительные схемы данных. + /// public readonly Dictionary Schemas = new() { - { "_subclass", new SchemaNode {Path = "_subclass"} } + ["_subclass"] = new() { Path = "_subclass" }, }; - private string[] _subclassSchemaTypes = + /// + /// Список типов, являющихся подклассами. + /// + private readonly string[] _subclassSchemaTypes = [ "research-field", "apparition", @@ -49,6 +63,11 @@ internal class AonprdSchemaBuilder(AonprdLoader loader) "epithet", ]; + /// + /// Выбор целевого элемента схемы данных для ввода. + /// + /// Входные данные. + /// Целевой элемент схемы данных. private SchemaNode GetSchema(AonprdLoader.LoaderEntry entry) { string key = entry.Type; @@ -58,7 +77,7 @@ internal class AonprdSchemaBuilder(AonprdLoader loader) return value; } - value = new SchemaNode + value = new() { // Resolver = Resolver, Path = key, @@ -68,6 +87,9 @@ internal class AonprdSchemaBuilder(AonprdLoader loader) return value; } + /// + /// Построение схемы данных. + /// public void BuildSchemas() { Console.WriteLine("Building schemas..."); @@ -89,6 +111,10 @@ internal class AonprdSchemaBuilder(AonprdLoader loader) bar.Refresh(Loader.ItemCount, "Done!"); } + /// + /// Сохранение постренных схем данных. + /// + /// Целевой каталог. public void Save(string targetDir) { Console.WriteLine("Dumping schemas..."); @@ -105,6 +131,12 @@ internal class AonprdSchemaBuilder(AonprdLoader loader) } } + /// + /// Сохранение отдельной схемы данных. + /// + /// Целевой каталог. + /// Название схемы. + /// Сохраняемая схема. protected void SaveSchema(string dir, string name, SchemaNode schema) { char[] invalids = Path.GetInvalidFileNameChars(); diff --git a/Pf2eDataScrapper/Importers/ProcessorInput.cs b/Pf2eDataScrapper/Importers/ProcessorInput.cs index 50a04a3..85081e7 100644 --- a/Pf2eDataScrapper/Importers/ProcessorInput.cs +++ b/Pf2eDataScrapper/Importers/ProcessorInput.cs @@ -4,10 +4,23 @@ using Pf2eModel.Model.Common.I18n; namespace Pf2eDataScrapper.Importers; +/// +/// Класс для работы с JSON-объектом. +/// +/// JSON-объект, который будет использоваться для доступа к данным. internal class ProcessorInput(JObject input) { - public readonly JObject Input = input; + /// + /// Входящий JSON-объект, который содержит данные для обработки. + /// + public JObject Input { get; } = input; + /// + /// Значение из JSON по заданному пути, которое обязательно должно существовать. + /// + /// Путь в JSON. + /// Объект типа `T` или `null`, если не удалось найти значение. + /// Если значение не найдено. public virtual T GetRequired(string path) { JToken token = @@ -16,6 +29,11 @@ internal class ProcessorInput(JObject input) return token.ToObject() ?? throw new NullReferenceException($"'{path}' is null"); } + /// + /// Значение из JSON по заданному пути, которое может не существовать. + /// + /// Путь в JSON + /// Объект типа `T` или `null`, если значение не найдено public virtual T? GetOptional(string path) { JToken? token = Input.SelectToken(path) ?? null; @@ -25,6 +43,12 @@ internal class ProcessorInput(JObject input) : token.ToObject() ?? default; } + /// + /// Значение из JSON по заданному пути с возможностью предоставить значение по умолчанию. + /// + /// Путь в JSON. + /// Значение, которое возвращается, если значение не найдено. + /// Объект типа `T` или `defaultValue`, если значение не найдено. public virtual T GetOptional(string path, T defaultValue) { JToken? token = Input.SelectToken(path) ?? null; @@ -35,6 +59,11 @@ internal class ProcessorInput(JObject input) return token.ToObject() ?? defaultValue; } + /// + /// Преобразование значения из JSON по заданному пути в перечисление с использованием `PgName` атрибута. + /// + /// Путь в JSON. + /// Перечисление T или выбрасывает исключение, если значение не соответствует жодному варианту. public virtual T EnumRequired(string path) where T : Enum { @@ -45,6 +74,12 @@ internal class ProcessorInput(JObject input) return Enum(value); } + /// + /// Преобразование значения из JSON по заданному пути в перечисление с возможностью предоставить значение по умолчанию. + /// + /// Путь в JSON + /// Значение, которое возвращается, если значение не найдено. + /// Значение перечисления T или defaultValue public virtual T EnumOptional(string path, T defaultValue) where T : Enum { @@ -65,11 +100,23 @@ internal class ProcessorInput(JObject input) } } + /// + /// Преобразование значения из JSON по заданному пути в строку локализации для заданного типа локализации. + /// + /// Путь в JSON. + /// Тип локализации. + /// Объект с добавленным значением для заданного типа. public virtual LocaleString GetRequiredLocale(string path, LocaleType type) { return new() { [type] = GetRequired(path) }; } + /// + /// Преобразование значения из JSON по заданному пути в строку локализации для заданного типа локализации. + /// + /// Путь в JSON. + /// Тип локализации. + /// Объект или null, если значение не найдено. public virtual LocaleString? GetOptionalLocale(string path, LocaleType type) { string? value = GetOptional(path); @@ -80,6 +127,11 @@ internal class ProcessorInput(JObject input) return new() { [type] = value }; } + /// + /// Преобразование значения из JSON в перечисление. + /// + /// Значение, которое нужно преобразовать + /// Объект типа `T`, если преобразование успешно, иначе выбрасывает исключение public virtual T Enum(string value) where T : Enum { @@ -104,6 +156,11 @@ internal class ProcessorInput(JObject input) throw new InvalidEnumStringException(typeof(T), value); } + /// + /// Исключение при неверном значении для перечисленния. + /// + /// Тип перечисленния. + /// Некорректное значение. public class InvalidEnumStringException(Type type, string value) : Exception($"Invalid value '{value}' for enum type {type.FullName}.") { } } diff --git a/Pf2eDataScrapper/Importers/Repositories/AbstractImporterRepository.cs b/Pf2eDataScrapper/Importers/Repositories/AbstractImporterRepository.cs index 4f4298f..a3dedef 100644 --- a/Pf2eDataScrapper/Importers/Repositories/AbstractImporterRepository.cs +++ b/Pf2eDataScrapper/Importers/Repositories/AbstractImporterRepository.cs @@ -1,12 +1,26 @@ namespace Pf2eDataScrapper.Importers.Repositories; +/// +/// Класс абстрактного репозитория. +/// +/// Импортёр, использующий репозиторий. internal abstract class AbstractImporterRepository(AbstractImporter importer) { + /// + /// Импортёр, использующий репозиторий. + /// public readonly AbstractImporter Importer = importer; + /// public string RepositoryDirPath => Importer.Dir; + /// + /// CLR-представкление директории с данными. + /// public DirectoryInfo RepositoryDir => new(RepositoryDirPath); + /// + /// Обновление данных в репозитории. + /// public abstract void Update(); } diff --git a/Pf2eDataScrapper/Importers/Repositories/GitRepository.cs b/Pf2eDataScrapper/Importers/Repositories/GitRepository.cs index c9c609d..d0974eb 100644 --- a/Pf2eDataScrapper/Importers/Repositories/GitRepository.cs +++ b/Pf2eDataScrapper/Importers/Repositories/GitRepository.cs @@ -2,27 +2,31 @@ namespace Pf2eDataScrapper.Importers.Repositories; +/// +/// Репозиторий, загружающий данные из GIT-источника. +/// +/// Импортёр, использующий репозиторий. +/// Ссылка на GIT-репозиторий. internal class GitRepository(AbstractImporter importer, string url) : AbstractImporterRepository(importer) { - public readonly string Url = url; - /// - /// Обновление импортёра + /// Ссылка на GIT-репозиторий. /// + public string Url { get; } = url; + + /// public override void Update() { - string gitPath = Path.Combine(RepositoryDirPath, Importer.Slug); - - if (Directory.Exists(gitPath)) + if (Directory.Exists(RepositoryDirPath)) { // git pull Process process = new() { - StartInfo = new ProcessStartInfo() + StartInfo = new() { FileName = "git", - WorkingDirectory = gitPath, + WorkingDirectory = RepositoryDirPath, Arguments = "pull", }, }; @@ -33,10 +37,10 @@ internal class GitRepository(AbstractImporter importer, string url) // git clone Process process = new() { - StartInfo = new ProcessStartInfo() + StartInfo = new() { FileName = "git", - WorkingDirectory = RepositoryDirPath, + WorkingDirectory = EnvSettings.DataDirPath, Arguments = $"clone {Url} {Importer.Slug}", }, }; diff --git a/Pf2eDataScrapper/Pf2eDatabase.cs b/Pf2eDataScrapper/Pf2eDatabase.cs index e819070..b3197f6 100644 --- a/Pf2eDataScrapper/Pf2eDatabase.cs +++ b/Pf2eDataScrapper/Pf2eDatabase.cs @@ -3,11 +3,24 @@ using Npgsql; namespace Pf2eDataScrapper; +/// +/// Main class for database connection and operations. +/// public partial class Pf2eDatabase { - public NpgsqlConnection Connection; + /// + /// Подключение к базе данных. + /// + public readonly NpgsqlConnection Connection; + /// + /// Имя пользователя для подключения к базе данных. + /// public readonly string Username; + + /// + /// Название базы данных. + /// public readonly string DatabaseName; private Pf2eDatabase() @@ -15,7 +28,7 @@ public partial class Pf2eDatabase Username = UsernamePattern().Match(EnvSettings.DatabaseConnection).Groups[1].Value; DatabaseName = DatabaseNamePattern().Match(EnvSettings.DatabaseConnection).Groups[1].Value; - Connection = new NpgsqlConnection(EnvSettings.DatabaseConnection); + Connection = new(EnvSettings.DatabaseConnection); Connection.Open(); } @@ -24,10 +37,19 @@ public partial class Pf2eDatabase Connection.Close(); } + /// + /// Singleton instance of the database connection manager. + /// public static Pf2eDatabase Instance { get; } = new(); + /// + /// Current transaction associated with this database connection. + /// public NpgsqlTransaction? Transaction { get; private set; } + /// + /// Begin a new transaction on the database connection. + /// public void StartTransaction() { if (Transaction != null) @@ -38,6 +60,9 @@ public partial class Pf2eDatabase Transaction = Connection.BeginTransaction(); } + /// + /// Commit the current transaction to the database. + /// public void Commit() { if (Transaction == null) @@ -48,6 +73,10 @@ public partial class Pf2eDatabase Transaction.Commit(); } + /// + /// Execute a SQL command against the database. + /// + /// The SQL command to execute public void ExecuteCommand(string sql) { NpgsqlCommand command = GetCommand(sql); @@ -55,9 +84,14 @@ public partial class Pf2eDatabase command.ExecuteNonQuery(); } + /// + /// Creates a new SQL command for the database connection. + /// + /// The SQL command string + /// A new NpgsqlCommand object public NpgsqlCommand GetCommand(string sql) { - return new NpgsqlCommand(sql, Connection); + return new(sql, Connection); } /* diff --git a/Pf2eDataScrapper/Program.cs b/Pf2eDataScrapper/Program.cs index 4c6da37..d32ba97 100644 --- a/Pf2eDataScrapper/Program.cs +++ b/Pf2eDataScrapper/Program.cs @@ -1,7 +1,7 @@ using Pf2eDataScrapper; using Pf2eDataScrapper.Importers.Implementations.Aonprd; -//Console.SetWindowSize(Console.LargestWindowWidth - 20, Console.LargestWindowHeight - 10); +// Console.SetWindowSize(Console.LargestWindowWidth - 20, Console.LargestWindowHeight - 10); EnvSettings.Load();