Глобальный рефакторинг пространств имён

This commit is contained in:
2025-10-02 13:29:30 +04:00
parent d23d81fb53
commit 2fa94851cb
3176 changed files with 27146 additions and 30584 deletions
@@ -0,0 +1,42 @@
using Microsoft.Extensions.DependencyInjection;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Compendium;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd;
using StringHelper = Helpers.StringHelper;
/// <summary>
/// Класс обработки данных с сайта Архивов Нефеса.
/// </summary>
internal class AonprdImporter : AbstractImporter
{
/// <summary>
/// Название внешней ссылки.
/// </summary>
public const string TargetName = "Archives of Nethys Pathfinder 2e";
/// <inheritdoc/>
public override string Name => TargetName;
/// <inheritdoc/>
public override string Slug => "aonprd";
#region "Внедрение зависимостей"
public static void RegisterServices(IServiceCollection services)
{
services.AddSingleton<AonprdImporter>();
services.AddSingleton<AonprdRepository>();
services.AddSingleton<AonprdCompendium>();
services.AddSingleton<AonprdBuilderGlobals>();
services.AddSingleton<RuleTocCollection>();
services.AddTransient<StringHelper>();
AonprdProcessorInputExtension.RegisterServices(services);
}
#endregion
}
@@ -0,0 +1,131 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;
using Pf2e.Scrapper.Helpers;
using Pf2e.Scrapper.Importers.Repositories;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd;
/// <summary>
/// Основной класс для работы с репозиторием данных Aonprd.
/// </summary>
internal class AonprdRepository(AonprdImporter importer) : AbstractImporterRepository(importer)
{
/// <summary>
/// URL для доступа к API Aonprd.
/// </summary>
public const string SearchUrl = "https://elasticsearch.aonprd.com";
/// <summary>
/// Текущая ревизия данных. Значение обновляется при обновлении репозитория.
/// </summary>
public int Revision { get; private set; }
/// <summary>
/// Метод для загрузки данных из API Aonprd.
/// </summary>
private string Load(string url)
{
if (!url.StartsWith("http://") && !url.StartsWith("https://"))
{
url = SearchUrl + url;
}
using HttpClient client = new();
using Task<Stream> stream = client.GetStreamAsync(url);
using StreamReader reader = new(stream.Result);
return reader.ReadToEnd();
}
/// <inheritdoc/>
public override void Update(ProcessProgress progress)
{
if (CheckRevision())
{
progress.Local.Complete();
progress.Information($"Данные Aonprd актуальны, текущая ревизия {Revision}.");
return;
}
progress.Information(
$"Данные Aonprd устарели, обновляем {RepositoryDirPath} до ревизии {Revision}..."
);
progress.Local.Reset(1);
if (Directory.Exists(RepositoryDirPath))
{
Directory.Delete(RepositoryDirPath, true);
}
Directory.CreateDirectory(RepositoryDirPath);
WriteFile("revision.txt", Revision.ToString());
progress.Local.Start("Загрузка индекса...");
string indexContent = Load($"/json-data/aon{Revision}-index.json");
WriteFile("index.json", indexContent);
JObject indexJson = JObject.Parse(indexContent);
progress.Local.Reset(indexJson.Count + 1);
progress.Local.End();
foreach (KeyValuePair<string, JToken?> kvp in indexJson)
{
string fileName = kvp.Key;
progress.Local.Start(fileName);
string fileContent = Load($"/json-data/{fileName}.json");
WriteFile($"{fileName}.json", fileContent);
progress.Local.End();
}
progress.Local.Complete();
}
/// <summary>
/// Проверка актуальности данных.
/// </summary>
/// <returns>true - актуальная ревизия, false - устаревшая/отсутсвует.</returns>
private bool CheckRevision()
{
JObject page = JObject.Parse(Load("/aon/_doc/feat-1"));
Revision = Convert.ToInt32(
page.SelectToken("_index")?.ToObject<string>()?.Replace("aon", "")
?? throw new("Не удаётся получить ревизию Архивов Нефиса.")
);
string revisionFile = Path.Combine(RepositoryDirPath, "revision.txt");
if (!File.Exists(revisionFile))
{
return false;
}
using StreamReader reader = new(revisionFile);
string lastRevision = reader.ReadToEnd();
return lastRevision == Revision.ToString();
}
/// <summary>
/// Запись данных в файл.
/// </summary>
/// <param name="path">Путь к файлу.</param>
/// <param name="content">Содержимое, которе требуется записать в файл.</param>
private void WriteFile(string path, string content)
{
string filePath = Path.Combine(RepositoryDirPath, path);
if (File.Exists(filePath))
{
File.Delete(filePath);
}
using StreamWriter writer = new(filePath);
writer.Write(content);
}
}
@@ -0,0 +1,68 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Newtonsoft.Json.Linq;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Compendium;
/// <summary>
/// Класс хранения данных компендиума.
/// </summary>
internal sealed class AonprdCompendium(AonprdRepository repository) : AbstractCompendium
{
/// <inheritdoc/>
public override AonprdRepository Repository { get; } = repository;
/// <summary>
/// Коллекция всех записей компендиума.
/// </summary>
public ConcurrentBag<AonprdCompendiumItem> Items { get; } = [];
/// <inheritdoc/>
public override int Count(params ICollection<string> types) =>
types.Count == 0 ? Items.Count : Items.Count(i => types.Contains(i.Type));
/// <inheritdoc/>
public override IEnumerable<Tuple<AbstractCompendiumItem, JObject>> Iterate(
params ICollection<string> types
)
{
IEnumerable<string> files =
types.Count == 0
? Items.Select(item => item.File).Distinct()
: Items
.Where(item => types.Contains(item.Type))
.Select(item => item.File)
.Distinct();
foreach (string file in files)
{
Dictionary<string, AonprdCompendiumItem> fileReferences = Items
.Where(i => i.File == file)
.ToDictionary(i => i.Id);
using StreamReader fileReader = new(
Path.Combine(Repository.RepositoryDirPath, $"{file}.json")
);
JArray array = JArray.Parse(fileReader.ReadToEnd());
foreach (JToken item in array)
{
string? id = item.SelectToken("id")?.ToString();
if (
id is null
|| !fileReferences.TryGetValue(id, out AonprdCompendiumItem? reference)
|| (types.Count > 0 && !types.Contains(reference.Type))
)
{
continue;
}
yield return new(reference, (JObject)item);
}
}
}
}
@@ -0,0 +1,43 @@
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Compendium;
/// <summary>
/// Ссылка на сущность с указанием файла, в котором она хранится.
/// </summary>
/// <param name="id">Идентификатор сущности.</param>
/// <param name="file">Имя файла.</param>
internal class AonprdCompendiumItem(string id, string file) : AbstractCompendiumItem
{
/// <summary>
/// Идентификатор сущности.
/// </summary>
public override string Id { get; } = id;
/// <summary>
/// Тип сущности.
/// </summary>
public override string Type { get; } = ResolveType(id);
/// <summary>
/// Имя файла, в котором хранится сущность.
/// </summary>
public string File { get; } = file;
/// <summary>
/// Определение типа сущности как префикса идентификатора.
/// </summary>
public static string ResolveType(string id)
{
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")
? $"{cat}-bonus"
: $"{cat}-sub"
: null;
}
}
}
@@ -0,0 +1,115 @@
using System.Collections.Generic;
using System.IO;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Pf2e.Database.Model.Base.Targets;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
/// <summary>
/// Класс-помощник, хранящий необходимые глобальные данные.
/// </summary>
internal class AonprdBuilderGlobals
{
/// <summary>
/// Цель внешних ссылок Архивов Нефисов.
/// </summary>
public ExternalTarget AonprdTarget { get; set; } = null!;
/// <summary>
/// Словарь идентификаторов источников.
/// </summary>
public Dictionary<string, int> SourceIds { get; } = [];
/// <summary>
/// Словарь идентификаторов признаков.
/// </summary>
public Dictionary<string, int> TraitIds { get; } = [];
/// <summary>
/// Список идентификаторов сущностей, которые нужно игнорировать при обработке.
/// </summary>
public List<string> Ignore { get; } = [];
/// <summary>
/// Словарей суффиксов для сущностей
/// </summary>
public Dictionary<string, string> SlugSuffixes { get; } = [];
/// <summary>
/// Словарь допольнительных проверок связей.
/// Ключ - устаревшая сущность, значение - обновлённая.
/// </summary>
public Dictionary<string, string> LegacyRemasterExtra { get; } = [];
/// <summary>
/// Словарь сущностей, которые нужно слить с другой/другими.
/// Ключ - пропускаемая сущность. Значение - целевая сущность.
/// </summary>
public Dictionary<string, string> MergeSources { get; } = [];
/// <summary>
/// Словарь сущностей, которые нужно слить с другой/другими.
/// Ключ - пропускаемая сущность. Значение - целевая сущность.
/// </summary>
public Dictionary<string, string> SubclassGroups { get; } = [];
/// <summary>
/// Конструктор класса <see cref="AonprdBuilderGlobals"/>.
/// </summary>
public AonprdBuilderGlobals()
{
using StreamReader fileReader = new("Resources\\ImportAdaptation.json");
JObject? json = JObject
.Parse(fileReader.ReadToEnd())
.SelectToken("Aonprd")
?.ToObject<JObject>();
if (json is not null)
{
Ignore = json.SelectToken("Ignore")?.ToObject<List<string>>() ?? [];
SlugSuffixes = JsonToDictionary(json, "SlugSuffixes");
LegacyRemasterExtra = JsonToDictionary(json, "LegacyRemasterExtra");
MergeSources = JsonToDictionary(json, "MergeSources");
SubclassGroups = JsonToDictionary(json, "SubclassGroups");
}
}
/// <summary>
/// Получить суффикс для ссылки.
/// </summary>
/// <param name="id">Идентификатор Aonprd.</param>
/// <returns>Суффикс для ссылки или пустая строка.</returns>
public string GetSlugSuffix(string id)
{
return SlugSuffixes.TryGetValue(id, out string? slug) ? $"-{slug}" : string.Empty;
}
/// <summary>
/// Преобразовать JSON-объект в словарь.
/// </summary>
/// <param name="json">Исходный JSON-объект.</param>
/// <param name="tokenPath">Путь к токену в JSON.</param>
/// <returns>Результирующий словарь.</returns>
public static Dictionary<string, string> JsonToDictionary(JObject json, string tokenPath)
{
return json.SelectToken(tokenPath)?.ToObject<Dictionary<string, string>>() ?? [];
}
}
/*
Проверка уникальности:
SELECT
E.type,
E.slug,
COUNT(*),
string_agg(E.id::text, ', ' ORDER BY E.id),
string_agg(EE.external_id::text, ', ' ORDER BY E.id)
FROM public.entities AS E
INNER JOIN public.entity_externals AS EE ON EE.entity_id = E.id
GROUP BY E.type, E.slug
HAVING COUNT(*) > 1 AND E.type NOT IN ('rule', 'action')
*/
@@ -0,0 +1,197 @@
using System.Collections.Generic;
using Microsoft.Extensions.DependencyInjection;
using Pf2e.Scrapper.Processors;
using Pf2e.Database.Model.Enum;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
internal static class AonprdProcessorInputExtension
{
/// <summary>
/// Обработать строку, содержащую информацию о количестве действий.
/// </summary>
/// <param name="_"></param>
/// <param name="input">Входная строка с данными.</param>
/// <returns>Структура, хранящая информацию о количетсве действий.</returns>
public static ActionStruct ResolveActionString(this ProcessorInput _, string input)
{
ActionStruct result = new();
switch (input)
{
case "Single Action":
result.Actions = result.MaxActions = 1;
break;
case "Two Actions":
result.Actions = result.MaxActions = 2;
break;
case "Three Actions":
result.Actions = result.MaxActions = 3;
break;
case "Reaction":
result.IsReaction = true;
break;
case "Free Action":
result.IsFreeAction = false;
break;
case "Single Action to Three Actions":
case "Single Action or more Actions":
result.Actions = 1;
result.MaxActions = 3;
break;
case "Single Action or Two Actions":
case "Single Action to Two Actions":
result.Actions = 1;
result.MaxActions = 2;
break;
case "Two Actions or Three Actions":
result.Actions = 2;
result.MaxActions = 3;
break;
case "Two Actions to 2 rounds":
result.Actions = 2;
result.MaxActions = 3;
result.TwoRounds = true;
break;
case "Reaction or One Action":
result.IsReaction = true;
result.Actions = result.MaxActions = 1;
break;
case "Reaction or Two Actions":
result.IsReaction = true;
result.Actions = result.MaxActions = 2;
break;
case "Free Action or One Action":
result.IsFreeAction = false;
result.Actions = result.MaxActions = 1;
break;
default:
result.IsPassive = true;
break;
}
return result;
}
/// <summary>
/// Класс структуры, хранящей информацию о количетсве действий.
/// </summary>
public class ActionStruct
{
/// <summary>
/// Количество действий
/// </summary>
public int Actions { get; set; }
/// <summary>
/// Максимальное количество действий
/// </summary>
public int MaxActions { get; set; }
/// <summary>
/// Флаг, указывающий, что действия не требуются.
/// </summary>
public bool IsPassive { get; set; }
/// <summary>
/// Флаг, указывающий, что действие может быть использовано как реакция.
/// </summary>
public bool IsReaction { get; set; }
/// <summary>
/// Флаг, указывающий, что можно использовать свободное действие.
/// </summary>
public bool IsFreeAction { get; set; }
/// <summary>
/// Флаг, указывающий, что действие может быть продлено до 2 раундов.
/// </summary>
public bool TwoRounds { get; set; }
}
public class SourceCategorySolver : EnumSolver<SourceCategory>
{
public override Dictionary<string, SourceCategory> Data { get; } =
new()
{
{ "Rulebooks", SourceCategory.Rulebooks },
{ "Adventure Paths", SourceCategory.AdventurePaths },
{ "Lost Omens", SourceCategory.LostOmens },
{ "Adventures", SourceCategory.Adventures },
{ "Comics", SourceCategory.Comics },
{ "Blog Posts", SourceCategory.BlogPosts },
{ "Society", SourceCategory.Society },
};
}
public class VisionEnumSolver : EnumSolver<VisionEnum>
{
public override Dictionary<string, VisionEnum> Data { get; } =
new()
{
{ "Low-Light VisionEnum", VisionEnum.LowLightVision },
{ "Darkvision", VisionEnum.DarkVision },
};
}
public class AttributeValuesSolver : EnumSolver<AttributeValues>
{
public override Dictionary<string, AttributeValues> Data { get; } =
new()
{
{ "Consitution", AttributeValues.Constitution },
{ "Other", AttributeValues.Free },
{ "other", AttributeValues.Free },
};
}
public class ActionEnumSolver : EnumSolver<ActionEnum>
{
public override Dictionary<string, ActionEnum> Data { get; } =
new()
{
{ "Single Action", ActionEnum.SingleAction },
{ "Two Actions", ActionEnum.TwoActions },
{ "Three Actions", ActionEnum.ThreeActions },
{ "Reaction", ActionEnum.Reaction },
{ "Free Action", ActionEnum.FreeAction },
{ "Single Action to Three Actions", ActionEnum.SingleActionToThreeActions },
{ "Single Action or more Actions", ActionEnum.SingleActionToThreeActions },
{ "Single Action or Two Actions", ActionEnum.SingleActionToTwoActions },
{ "Single Action to Two Actions", ActionEnum.SingleActionToTwoActions },
{ "Two Actions or Three Actions", ActionEnum.TwoActionsToThreeActions },
{ "Two Actions to 2 rounds", ActionEnum.TwoActionsToTwoRounds },
{ "Reaction or One Action", ActionEnum.ReactionOrOneAction },
{ "Reaction or Two Actions", ActionEnum.ReactionOrTwoAction },
{ "Reaction or Three Actions", ActionEnum.ReactionOrThreeAction },
{ "Free Action or One Action", ActionEnum.FreeActionOrOneAction },
{ "1 Minute", ActionEnum.OneMinute },
{ "1 minute", ActionEnum.OneMinute },
{ "5 minutes", ActionEnum.FiveMinutes },
{ "10 minutes", ActionEnum.TenMinutes },
{ "30 minutes", ActionEnum.ThirtyMinutes },
{ "1 hour", ActionEnum.OneHour },
{ "<i>Nethys Note: No cast time was printed</i>", ActionEnum.Unknown },
};
}
public static void RegisterServices(IServiceCollection serviceCollection)
{
serviceCollection.AddTransient<IEnumSolver, SourceCategorySolver>();
serviceCollection.AddTransient<IEnumSolver, VisionEnumSolver>();
serviceCollection.AddTransient<IEnumSolver, AttributeValuesSolver>();
serviceCollection.AddTransient<IEnumSolver, ActionEnumSolver>();
}
}
@@ -0,0 +1,341 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Pf2e.Database.Model.Base.Source;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
/// <summary>
/// Класс коллекции книг, содержащих правила.
/// Реализует шаблон <c>Singleton</c>.
/// </summary>
internal class RuleTocCollection
{
/// <summary>
/// Список книг, содержащих правила.
/// </summary>
public List<RuleTocBook> Books { get; } = [];
/// <summary>
/// Поиск книги по числовому идентификатору.
/// </summary>
public RuleTocBook this[int id] =>
Books.FirstOrDefault(i => i.SourceId == id) ?? throw new($"Book with id {id} not found");
/// <summary>
/// Поиск книги по CLR-представлению источника.
/// </summary>
public RuleTocBook this[Source source]
{
get
{
RuleTocBook? result = Books.FirstOrDefault(i => i.SourceId == source.Id);
if (result is not null)
{
return result;
}
result = new(source);
Books.Add(result);
return result;
}
}
/// <summary>
/// Поиск книги по имени.
/// </summary>
public RuleTocBook? FindName(string name)
{
foreach (RuleTocBook book in Books)
{
if (book.IsNameExists(name))
{
return book;
}
}
return null;
}
/// <summary>
/// Поиск правила по идентификатору.
/// </summary>
public RuleTocItem? FindEntityId(int id)
{
foreach (RuleTocBook book in Books)
{
RuleTocItem? result = book.GetByEntityId(id);
if (result is not null)
{
return result;
}
}
return null;
}
/// <summary>
/// Возвращает строковое представление коллекции в файле YAML.
/// </summary>
public override string ToString()
{
return string.Join("\n", Books.Select(book => book.ToString()).ToList());
}
/// <summary>
/// Сохраняет коллекцию в файле YAML.
/// </summary>
public void SaveFile()
{
string fileName =
$"rules-toc.{new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds()}.debug.yaml";
using FileStream fileStream = File.Create(fileName);
using StreamWriter writer = new(fileStream);
writer.Write(ToString());
}
/// <summary>
/// Экземпляр класса <see cref="RuleTocCollection"/>.
/// </summary>
public static RuleTocCollection Instance { get; } = new();
}
/// <summary>
/// Класс представляет собой книгу, содержащую правила.
/// </summary>
internal class RuleTocBook(Source source)
{
/// <summary>
/// Идентификатор источника.
/// </summary>
public int SourceId { get; } = source.Id;
/// <summary>
/// Названия источника.
/// </summary>
public string SourceName { get; } = source.Name;
/// <summary>
/// Список дочерних правил.
/// </summary>
/// <summary>
/// Список дочерних элементов.
/// </summary>
public List<RuleTocItem> Children { get; } = [];
/// <summary>
/// Находит элемент по хлебным крошкам.
/// </summary>
/// <param name="breadcrumbs">Массив хлебных крошек, указывающих на путь в дереве.</param>
/// <returns><see cref="RuleTocItem"/> или <c>null</c> если элемент не найден.</returns>
public RuleTocItem Get(string[] breadcrumbs)
{
RuleTocItem? node = null;
while (true)
{
List<RuleTocItem> children = node?.Children ?? Children;
if (breadcrumbs.Length == 0)
{
return node!;
}
string root = breadcrumbs[0];
RuleTocItem? item = children.FirstOrDefault(i => i.Name == root);
if (item is null)
{
item = new(root, node);
children.Add(item);
}
breadcrumbs = breadcrumbs[1..];
node = item;
}
}
/// <summary>
/// Проверяет, существует ли имя в текущей книге или дочерних элементах.
/// </summary>
/// <param name="name">Имя для проверки.</param>
/// <param name="node">Начальный элемент для поиска.</param>
/// <returns>true если имя.</returns>
public bool IsNameExists(string name, RuleTocItem? node = null)
{
foreach (RuleTocItem item in node?.Children ?? Children)
{
if (item.Name == name || IsNameExists(name, item))
{
return true;
}
}
return false;
}
/// <summary>
/// Находит элемент по идентификатору сущности.
/// </summary>
/// <param name="id">Идентификатор сущности.</param>
/// <param name="node">Начальный узел для поиска.</param>
/// <returns><see cref="RuleTocItem"/> или <c>null</c> если элемент не найден.</returns>
public RuleTocItem? GetByEntityId(int id, RuleTocItem? node = null)
{
if (node?.RuleId == id)
{
return node;
}
foreach (RuleTocItem item in node?.Children ?? Children)
{
RuleTocItem? result = GetByEntityId(id, item);
if (result is not null)
{
return result;
}
}
return null;
}
/// <summary>
/// Возвращает строковое представление книги в файле YAML.
/// </summary>
/// <returns>Строковое представление книги.</returns>
public override string ToString()
{
return $"\"{SourceName}\":\n"
+ string.Join(
"\n",
Children.Select(child => RuleTocItem.Indent(child.ToString())).ToList()
);
}
}
/// <summary>
/// Класс представляет собой элемент дерева правил.
/// </summary>
internal class RuleTocItem(string name, RuleTocItem? parent)
{
public string? Id { get; set; } = null;
/// <summary>
/// Имя элемента.
/// </summary>
public string Name { get; } = name;
/// <summary>
/// Идентификатор правила.
/// </summary>
public int? RuleId { get; set; } = null;
/// <summary>
/// Указывает, что элемент обрабатывается.
/// </summary>
public bool IsHandled { get; set; } = false;
/// <summary>
/// Родительский элемент дерева.
/// </summary>
public RuleTocItem? Parent { get; } = parent;
/// <summary>
/// Порядок дочерних элементов.
/// </summary>
public List<string>? ChildrenOrder { get; set; } = null;
/// <summary>
/// Коллекция дочерних элементов.
/// </summary>
public List<RuleTocItem> Children { get; } = [];
/// <summary>
/// Предыдущий идентификатор.
/// </summary>
public string? PreviousId { get; set; }
/// <summary>
/// Следующий идентификатор.
/// </summary>
public string? NextId { get; set; }
/// <summary>
/// Идентификатор первого дочернего элемента.
/// </summary>
public string? FirstChildId { get; set; }
/// <summary>
/// Идентификатор последнего дочернего элемента.
/// </summary>
public string? LastChildId { get; set; }
/// <summary>
/// Возвращает строковое представление элемента содержания в файле YAML.
/// </summary>
/// <returns>Строковое представление элемента содержания.</returns>
public override string ToString()
{
List<string> rows =
[
$"{Name}:",
$"{Tab}aonprd: {Id ?? "null"}",
$"{Tab}entity: {RuleId?.ToString() ?? "null"}",
];
if (Children.Count == 0)
{
// rows.Add($"{TAB}children: None");
return string.Join("\n", rows);
}
rows.Add($"{Tab}children:");
foreach (RuleTocItem child in Children)
{
rows.Add(child.Indent(2));
}
return string.Join("\n", rows);
}
/// <summary>
/// Увеличение отступа строкового представления элемента содержания в файле YAML.
/// </summary>
/// <param name="amount">Количество отступов.</param>
/// <returns>Строковое представление элемента содержания с отступом.</returns>
/// <seealso cref="Tab"/>
public string Indent(int amount = 1)
{
string result = ToString();
for (int i = 0; i < amount; i++)
{
result = Indent(result);
}
return result;
}
/// <summary>
/// Увеличение отступа строки.
/// </summary>
/// <param name="str">Входная строка.</param>
/// <returns>Строка с отступом.</returns>
/// <seealso cref="Tab"/>
public static string Indent(string str)
{
return Tab + str.Replace("\n", $"\n{Tab}");
}
/// <summary>
/// Строка отступа строки.
/// </summary>
/// <seealso cref="Indent(int)"/>
/// <seealso cref="Indent(string)"/>
public const string Tab = " ";
}
@@ -0,0 +1,123 @@
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
/// <summary>
/// Класс-помощник, для обработки строкового ввода с Архивов Нефиса.
/// Реализует шаблон <c>Singleton</c>.
/// </summary>
internal partial class StringHelper
{
/// <summary>
/// Конструктор класса <see cref="StringHelper"/>
/// </summary>
public StringHelper() { }
/// <summary>
/// Разбиение строки по нестабильным строковым разделителям (например, <c>", "</c>, <c>" or "</c> и т.д.).
/// </summary>
/// <param name="value">Исходная строка.</param>
/// <returns>Коллекция строк.</returns>
public List<string> RowSplit(string value)
{
return value
.Replace(", or", ",")
.Replace(" or ", ", ")
.Replace(";", ",")
.Split(", ")
.Where(s => !string.IsNullOrWhiteSpace(s))
.ToList();
}
/// <summary>
/// Разбор списка ссылок в исходной строке.
/// </summary>
/// <param name="input">Исходная строка.</param>
/// <returns>Структура, содеражащая информацию разбиении строки по ссылкам.</returns>
public MarkdownLinkSplit ParseMarkdownLinks(string input)
{
MarkdownLinkSplit result = new();
MatchCollection matches = LinkRegex().Matches(input);
foreach (Match match in matches)
{
result.Links.Add(
new()
{
Prefix = match.Groups[1].Value.Trim(),
Text = match.Groups[2].Value.Trim(),
Url = match.Groups[3].Value.Trim(),
}
);
}
result.Suffix = LinkRegex().Replace(input, "");
return result;
}
/// <summary>
/// Извлечение суффикса из описания источника.
/// </summary>
/// <param name="input">Исходная строка.</param>
/// <returns>Суффикс.</returns>
public string ResolveSourceSuffix(string input)
{
Match match = SourceSuffixRegex().Match(input);
return match.Captures.Count > 0 ? match.Groups[1].Value : "";
}
/// <summary>
/// Регулярное выражение для извлечения источника из строки с Markdown-ссылкой.
/// </summary>
/// <returns>Регулярное выражение.</returns>
[GeneratedRegex(@"^\[.*?\]\(.*?\)\s+(.*)$")]
public static partial Regex SourceSuffixRegex();
/// <summary>
/// Регулярное выражение для выявления Markdown-ссылок в строке.
/// </summary>
/// <returns>Регулярное выражение.</returns>
[GeneratedRegex(@"(.*?)\[(.*?)\]\((.*?)\)")]
public static partial Regex LinkRegex();
}
/// <summary>
/// Класс структуры, содеражащая информацию разбиении строки по ссылкам.
/// </summary>
internal class MarkdownLinkSplit
{
/// <summary>
/// Список содержащихся ссылок.
/// </summary>
public List<MarkdownLink> Links { get; } = [];
/// <summary>
/// Завершающая часть ссылки.
/// </summary>
public string Suffix { get; set; } = "";
}
/// <summary>
/// Информация о ссылке в строке.
/// </summary>
internal class MarkdownLink
{
/// <summary>
/// Данные, содержащиеся до ссылки.
/// </summary>
public string Prefix { get; init; } = "";
/// <summary>
/// Текст ссылки.
/// </summary>
public string Text { get; init; } = "";
/// <summary>
/// URL ссылки.
/// </summary>
public string Url { get; init; } = "";
}
@@ -0,0 +1,282 @@
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Pf2e.Scrapper.Helpers;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2e.Scrapper.Processors;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Scrapper.Processors.Definitions;
using Pf2e.Database;
using Pf2e.Database.Model;
using Pf2e.Database.Model.Base;
using Pf2e.Database.Model.Base.Source;
using Pf2e.Database.Model.Base.Targets;
using Pf2e.Database.Model.Common.I18n;
using Pf2e.Database.Model.Enum;
using Pf2e.Database.Model.Structure;
using Helpers_StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
using StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors;
/// <summary>
/// Абстрактный класс-обработчик сущностей, полученных из Архивов Нефиса.
/// Класс предназначен для обработки данных с помощью различных обработчиков,
/// для которых указан атрибут <see cref="ProcessorAttribute"/>.
/// </summary>
/// <param name="input">Объект обработки вводимого JSON.</param>
/// <param name="stringHelper">[DI] Помощник обработки строк.</param>
/// <param name="helper">[DI] Помощник работы с БД.</param>
/// <param name="globals">[DI] Глобальные параметра обработки Архивов Нефиса.</param>
/// <seealso cref="ProcessorAttribute"/>
internal abstract class AbstractEntityProcessor(
ProcessorInput input,
Helpers_StringHelper stringHelper,
DatabaseHelper helper,
AonprdBuilderGlobals globals
) : AbstractInputProcessor(input)
{
/// <inheritdoc cref="Pf2eDbContext"/>
public Pf2eDbContext Context { get; } = new();
/// <inheritdoc cref="StringHelper"/>
public Helpers_StringHelper StringHelper { get; } = stringHelper;
/// <inheritdoc cref="DatabaseHelper"/>
public DatabaseHelper Helper { get; } = helper;
/// <inheritdoc cref="AonprdBuilderGlobals"/>
public AonprdBuilderGlobals Globals { get; } = globals;
/// <summary>
/// Корневая сущность.
/// </summary>
public Entity Entity { get; } = new();
/// <summary>
/// Тип сущности.
/// </summary>
public abstract EntityType Type { get; }
/// <summary>
/// Основная сущность.
/// </summary>
public abstract IEntity Object { get; }
/// <summary>
/// Название сущности, которое будет использоваться для обработки данных.
/// </summary>
public string Name
{
get { return _name ??= GetEntityName(); }
}
private string? _name = null;
/// <summary>
/// Добавление основной сущности в контекст базы данных.
/// </summary>
protected abstract void Add();
/// <inheritdoc/>
public override void Process()
{
Check();
AddEntity();
AddSource();
AddExternal();
AddTraits();
}
/// <inheritdoc/>
public override void Save()
{
Add();
Context.SaveChanges();
}
/// <summary>
/// Проверки необходимости импорта сущности.
/// </summary>
public virtual void Check()
{
string id = Input.GetRequired<string>("id");
if (Globals.Ignore.Contains(id))
{
throw new SkipItemException(Name, "игнорируется.");
}
if (Globals.MergeSources.ContainsKey(id))
{
throw new SkipItemException(Name, "помечено на слияние.");
}
}
#region "Генерация описания"
/// <summary>
/// Получение списка полей, которые должны быть добавлены в описание.
/// </summary>
/// <returns></returns>
// [Obsolete("Данный метод будет отключён на финальных этапах разработки.")]
protected virtual ICollection<string> AddedDescriptionPaths() => [];
/// <summary>
/// Создание строкового представления объекта по указанному пути для добавления к описанию сущности.
/// </summary>
/// <param name="path">Путь к добавляемому объекту.</param>
/// <returns>Строковое представление для добавления к описанию сущности.</returns>
// [Obsolete("Данный метод будет отключён на финальных этапах разработки.")]
protected string AddedDescription(string path) =>
$"\r\n\r\n========== [{path}] ==========\r\n\r\n{Input.GetOptional<object?>(path)?.ToString() ?? ""}";
/// <summary>
/// Создание строкового представления объектов по указанным путям для добавления к описанию сущности.
/// </summary>
/// <param name="paths">Список путей к добавляемым объектам.</param>
/// <returns>Строковое представление для добавления к описанию сущности.</returns>
// [Obsolete("Данный метод будет отключён на финальных этапах разработки.")]
protected string AddedDescription(ICollection<string> paths) =>
string.Join("", paths.Select(AddedDescription));
/// <summary>
/// Сгенерировать описание сущности.
/// </summary>
/// <returns>Описание сущности</returns>
protected virtual string GetDescription() =>
Input.GetRequired<string>("markdown") + AddedDescription(AddedDescriptionPaths());
/// <summary>
/// Генерация JSON-строки из объектов, располагаюхися по путям, указанным в <paramref name="paths"/>.
/// </summary>
/// <param name="paths">Коллекция путей, из которых необходимо достатить объекты.</param>
/// <returns>JSON-строка.</returns>
protected string GenerateJsonString(params ICollection<string> paths)
{
Dictionary<string, object?> data = [];
foreach (string key in paths)
{
data.Add(key, Input.Input.SelectToken(key));
}
return JsonConvert.SerializeObject(data, new JsonSerializerSettings());
}
#endregion
#region "Генерация сущности"
/// <summary>
/// Получает имя сущности из входных данных.
/// </summary>
/// <returns>Имя сущности.</returns>
protected virtual string GetEntityName() => Input.GetRequired<string>("name").Replace("’", "'");
/// <summary>
/// Генерация основных полей корневой сущности.
/// </summary>
protected virtual void AddEntity()
{
lock (Helper.IdLock)
{
Entity.Id = Helper.NextEntityId;
}
Entity.Type = Type;
Entity.Name[LocaleType.English] = Name;
Entity.Img = "";
Entity.Description = GetDescription();
Entity.Rarity = Input.EnumOptional("rarity", EntityRarity.Common);
Entity.Slug =
Helper.GenerateSlug(Entity.Name[LocaleType.English])
+ Globals.GetSlugSuffix(Input.GetRequired<string>("id"));
Entity.IsTranslated = false;
Entity.IsChecked = false;
Object.Entity = Entity;
Object.Id = Entity.Id;
Context.Entities.Add(Entity);
}
/// <summary>
/// Генерация источников сущности.
/// </summary>
protected virtual void AddSource()
{
string[] sourceNames = Input
.GetRequired<JArray>("source")
.Select(token => token.ToString())
.ToArray();
if (sourceNames.Length == 0)
{
throw new(
$"Источники для сущности \"{Entity.Name[LocaleType.English]}\" "
+ $"(Aonprd id \"{Input.GetRequired<string>("id")}\") не найдены."
);
}
foreach (string sourceName in sourceNames)
{
EntitySource entitySource = new()
{
Entity = Entity,
SourceId = Globals.SourceIds[sourceName.Trim()],
Suffix = StringHelper.ResolveSourceSuffix(
Input.GetRequired<string>("source_markdown")
),
IsRemaster = Input.GetOptional<JArray>("legacy_id") is not null,
};
Entity.Sources.Add(entitySource);
}
}
/// <summary>
/// Генерация внешних ссылок сущности.
/// </summary>
protected virtual void AddExternal()
{
EntityExternal external = new()
{
Entity = Entity,
TargetId = Globals.AonprdTarget.Id,
ExternalId = Input.GetRequired<string>("id"),
Suffix = Input.GetRequired<string>("url"),
Md5 = Input.Md5(),
};
Entity.Externals.Add(external);
Context.EntityExternals.Add(external);
}
/// <summary>
/// Генерация списка признаков сущности.
/// </summary>
protected virtual void AddTraits()
{
JToken? traitsJson = Input.Input.SelectToken("trait");
if (traitsJson?.Type != JTokenType.Array)
{
return;
}
List<string> names = traitsJson.ToArray().Select(jToken => jToken.ToString()).ToList();
List<int> traitIds = names.Select(name => Globals.TraitIds[name]).ToList();
List<Trait> traits = Context.Traits.Where(t => traitIds.Contains(t.Id)).ToList();
foreach (Trait trait in traits)
{
Entity.Traits.Add(trait);
}
}
#endregion
}
@@ -0,0 +1,131 @@
using System.Collections.Generic;
using System.Linq;
using Pf2e.Scrapper.Helpers;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2e.Scrapper.Processors;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Database;
using Pf2e.Database.Model.Base;
using Pf2e.Database.Model.Common;
using Pf2e.Database.Model.Common.I18n;
using Pf2e.Database.Model.Enum;
using Pf2e.Database.Model.Structure;
using Helpers_StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
using StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors;
/// <summary>
/// Абстрактный класс-обработчик сущностей подклассов, полученных из Архивов Нефиса.
/// Класс предназначен для обработки данных с помощью различных обработчиков,
/// для которых указан атрибут <see cref="ProcessorAttribute"/>.
/// </summary>
internal abstract class AbstractSubclassProcessor(
ProcessorInput input,
Helpers_StringHelper stringHelper,
DatabaseHelper helper,
AonprdBuilderGlobals globals
) : AbstractEntityProcessor(input, stringHelper, helper, globals)
{
/// <inheritdoc/>
public override EntityType Type => EntityType.Subclass;
/// <inheritdoc/>
public abstract override Subclass Object { get; }
/// <summary>
/// Название подкласса.
/// </summary>
public abstract string? ClassName { get; }
/// <summary>
/// Название группы подклассов.
/// </summary>
public abstract string GroupName { get; }
/// <inheritdoc/>
protected override void Add() => Context.Subclasses.Add(Object);
/// <inheritdoc/>
public override void Process()
{
base.Process();
Object.Group = GetGroup();
ProcessObject();
}
/// <summary>
/// Обработка полей подкласса.
/// </summary>
public abstract void ProcessObject();
/// <summary>
/// Получение CLR-представления группы подклассов, к которому относится обрабатываемый подкласс.
/// </summary>
/// <returns>CLR-представления группы подклассов.</returns>
public SubclassGroup GetGroup()
{
return Context.SubclassGroups.First(g =>
Pf2eDbContext.LocaleSearch(g.Entity.Name, GroupName)
);
}
/// <summary>
/// Добавление заклинаний из строкового представления.
/// </summary>
/// <param name="spellMd">Строковое представление списка заклинаний.</param>
public void AddSpells(string spellMd)
{
List<string> spellUrls = StringHelper
.ParseMarkdownLinks(spellMd)
.Links.Select(l => l.Url)
.ToList();
List<int> spellIds = Context
.EntityExternals.Where(x => spellUrls.Contains(x.Suffix))
.Select(x => x.Entity.Id)
.ToList();
List<Entity> spells = Context
.Spells.Where(s => spellIds.Contains(s.Id))
.Select(s => s.Entity)
.ToList();
foreach (Entity spellEntity in spells)
{
Object.Entities.Add(spellEntity);
}
}
/// <summary>
/// Добавление тренировок из строкового представления.
/// </summary>
/// <param name="trainingMd">Строковое представление списка тренировок.</param>
public void AddTrainings(string trainingMd)
{
List<string> trainingNames = trainingMd.Split(", ").ToList();
List<Training> trainings = Context
.Trainings.Where(t => Pf2eDbContext.LocaleSearchArray(t.Name, trainingNames))
.ToList();
foreach (string name in trainingNames)
{
Training? training = trainings.FirstOrDefault(t => t.Name.Contains(name));
training ??= new()
{
Name = new() { { LocaleType.English, name } },
Type = TrainingType.Lore,
};
Object.Trainings.Add(
new()
{
Subclass = Object,
Training = training,
Rank = TrainingRank.Trained,
}
);
}
}
}
@@ -0,0 +1,70 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json.Linq;
using Pf2e.Scrapper.Processors;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Scrapper.Processors.Definitions;
using Pf2e.Database;
using Pf2e.Database.Model.Base;
using Pf2e.Database.Model.Base.Targets;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Finalization;
[ProcessorCollection(
"aonprd:legacy-remaster",
InnerType = null,
CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Связь устаревших и обновлённых сущностей",
Group = ProcessorGroupEnum.AonprdFinalization,
Parallel = true
)]
internal class LegacyRemasterProcessors(ProcessorInput input) : AbstractInputProcessor(input)
{
private Pf2eDbContext Context { get; } = new();
public override void Process()
{
string aonprdId = Input.GetRequired<string>("id");
EntityExternal entity =
Context.EntityExternals.AsNoTracking().FirstOrDefault(x => x.ExternalId == aonprdId)
?? throw new SilentSkipItemException();
int entityId = entity.EntityId;
Entity legacyEntity =
Context.Entities.FirstOrDefault(e => e.Id == entityId)
?? throw new($"Неизвестная сущность с ID {entityId}");
legacyEntity.IsLegacy = true;
foreach (int remasterId in GetEntityIds("remaster_id"))
{
Context.LegacyRemasters.Add(new() { LegacyId = entityId, RemasterId = remasterId });
Entity remasterEntity =
Context.Entities.AsNoTracking().FirstOrDefault(e => e.Id == remasterId)
?? throw new($"Неизвестная сущность с ID {remasterId}");
if (legacyEntity.Slug == remasterEntity.Slug)
{
legacyEntity.Slug += "-legacy";
}
}
}
protected List<int> GetEntityIds(string path)
{
List<string> aonprdIds = (Input.GetOptional<JArray>(path) ?? [])
.Select(token => token.ToString())
.ToList();
return Context
.EntityExternals.AsNoTracking()
.Where(x => aonprdIds.Contains(x.ExternalId!))
.Select(x => x.EntityId)
.ToList();
}
public override void Save()
{
Context.SaveChanges();
}
}
@@ -0,0 +1,104 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2e.Scrapper.Processors;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Scrapper.Processors.Definitions;
using Pf2e.Scrapper.Processors.Factory;
using Pf2e.Database;
using Pf2e.Database.Model.Base;
using Pf2e.Database.Model.Base.Targets;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Finalization;
[ProcessorFactory(
"aonprd:fix-legacy-remaster",
ProcessorType = typeof(MissingLegacyRemasterProcessor),
Description = "Дополнение связяй устаревших и обновлённых сущностей",
Group = ProcessorGroupEnum.AonprdFinalization,
Parallel = true
)]
[ProcessorDependencies(["aonprd:legacy-remaster"])]
internal class MissingLegacyRemasterProcessorFactory(
IServiceProvider serviceProvider,
AonprdBuilderGlobals globals
) : AbstractProcessorFactory
{
public IServiceProvider ServiceProvider { get; } = serviceProvider;
public AonprdBuilderGlobals Globals { get; } = globals;
public override int Count() => Globals.LegacyRemasterExtra.Count;
public override IEnumerable<Tuple<string, IProcessor>> GetEnumerator()
{
foreach ((string key, string value) in Globals.LegacyRemasterExtra)
{
yield return new(
$"{key} -> {value}",
ActivatorUtilities.CreateInstance<MissingLegacyRemasterProcessor>(
ServiceProvider,
key,
value
)
);
}
}
}
internal class MissingLegacyRemasterProcessor(string legacyId, string remasterId) : IProcessor
{
public Pf2eDbContext Context { get; } = new();
public string LegacyId { get; } = legacyId;
public string RemasterId { get; } = remasterId;
private Entity GetEntity(string aonprdId)
{
EntityExternal entity =
Context.EntityExternals.AsNoTracking().FirstOrDefault(x => x.ExternalId == aonprdId)
?? throw new SilentSkipItemException();
int entityId = entity.EntityId;
return Context.Entities.FirstOrDefault(e => e.Id == entityId)
?? throw new($"Неизвестная сущность с ID {entityId}");
}
public void Process()
{
Entity legacyEntity = GetEntity(LegacyId);
Entity remasterEntity = GetEntity(RemasterId);
TestExist(legacyEntity, remasterEntity);
Context.LegacyRemasters.Add(
new() { LegacyId = legacyEntity.Id, RemasterId = remasterEntity.Id }
);
legacyEntity.IsLegacy = true;
if (legacyEntity.Slug == remasterEntity.Slug)
{
legacyEntity.Slug += "-legacy";
}
}
public void TestExist(Entity legacyEntity, Entity remasterEntity)
{
if (
Context
.LegacyRemasters.AsNoTracking()
.Any(x => x.LegacyId == legacyEntity.Id && x.RemasterId == remasterEntity.Id)
)
{
throw new SilentSkipItemException();
}
}
public void Save()
{
Context.SaveChanges();
}
}
@@ -0,0 +1,67 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Newtonsoft.Json.Linq;
using Pf2e.Scrapper.Helpers;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Compendium;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Scrapper.Processors.Definitions;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Preparation;
[Processor(
"aonprd:compendium",
Description = "Загрузка компендиума",
Group = ProcessorGroupEnum.AonprdPreparation
)]
[ProcessorDependencies(["aonprd:repository"])]
internal class CompendiumLoadProcessor(
AonprdRepository repository,
AonprdCompendium compendium,
ProcessProgress progress
) : IProcessor
{
public AonprdRepository Repository { get; } = repository;
public AonprdCompendium Compendium { get; } = compendium;
public ProcessProgress Progress { get; } = progress;
public void Process()
{
using StreamReader indexFile = new(
Path.Combine(Repository.RepositoryDirPath, "index.json")
);
JObject indexJson = JObject.Parse(indexFile.ReadToEnd());
Progress.Local.Reset(indexJson.Count);
foreach ((string file, JToken? value) in indexJson)
{
Progress.Local.Start(file);
JArray items = (JArray)value!;
foreach (JToken item in items)
{
AonprdCompendiumItem compendiumItem = new(item.ToString(), file);
Compendium.Items.Add(compendiumItem);
}
Progress.Local.End();
}
Progress.Local.Complete();
}
public void Save()
{
List<AonprdCompendiumItem> result = Compendium.Items.OrderBy(item => item.Id).ToList();
Compendium.Items.Clear();
foreach (AonprdCompendiumItem item in result)
{
Compendium.Items.Add(item);
}
}
}
@@ -0,0 +1,39 @@
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Scrapper.Processors.Definitions;
using Pf2e.Database;
using Pf2e.Database.Model.Base.Targets;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Preparation;
[Processor(
"aonprd:external",
Description = "Создание цели внешних ссылок",
Group = ProcessorGroupEnum.AonprdPreparation
)]
[ProcessorDependencies(["aonprd:repository"])]
internal class ExternalProcessor(AonprdBuilderGlobals globals) : AbstractSimpleProcessor
{
public Pf2eDbContext Context { get; } = new();
public AonprdBuilderGlobals Globals { get; } = globals;
public ExternalTarget AonprdTarget = null!;
public override void Process()
{
AonprdTarget = new()
{
Name = AonprdImporter.TargetName,
Icon = "http://www.google.com/s2/favicons?domain=2e.aonprd.com",
Prefix = "https://2e.aonprd.com",
};
}
public override void Save()
{
Context.ExternalTargets.Add(AonprdTarget);
Context.SaveChanges();
Globals.AonprdTarget = AonprdTarget;
}
}
@@ -0,0 +1,24 @@
using Pf2e.Scrapper.Helpers;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Scrapper.Processors.Definitions;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Preparation;
[Processor(
"aonprd:repository",
Description = "Обновление репозитория",
Group = ProcessorGroupEnum.AonprdPreparation
)]
internal class RepositoryUpdateProcessor(AonprdRepository repository, ProcessProgress progress)
: IProcessor
{
public AonprdRepository Repository { get; } = repository;
public ProcessProgress Progress { get; } = progress;
public void Process()
{
Repository.Update(Progress);
}
public void Save() { }
}
@@ -0,0 +1,97 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Pf2e.Scrapper.Helpers;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Compendium;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Scrapper.SchemaBuilder;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Preparation;
[Processor(
"aonprd:schema",
Description = "Построение схемы данных",
Group = ProcessorGroupEnum.AonprdPreparation,
Active = false
)]
[ProcessorDependencies(["aonprd:repository", "aonprd:compendium"])]
internal class SchemaBuildProcessor(
AonprdImporter importer,
AonprdCompendium compendium,
ProcessProgress progress,
ApplicationSettings settings
) : AbstractSchemaBuildProcessor
{
/// <inheritdoc cref="AonprdImporter"/>
protected AonprdImporter Importer { get; } = importer;
/// <inheritdoc cref="AonprdCompendium"/>
protected override AonprdCompendium Compendium { get; } = compendium;
/// <inheritdoc cref="ProcessProgress"/>
protected override ProcessProgress Progress { get; } = progress;
/// <inheritdoc cref="ApplicationSettings"/>
protected ApplicationSettings Settings { get; } = settings;
/// <inheritdoc/>
protected override string SavePath => Path.Combine(Settings.OutputDirPath, Importer.Slug);
/// <inheritdoc/>
protected override AonprdTypeResolver Resolver { get; } = new();
/// <summary>
/// Список типов, являющихся подклассами.
/// </summary>
private readonly string[] _subclassSchemaTypes =
[
"research-field",
"apparition",
"practice",
"instinct",
"muse",
"cause",
"tenet",
"doctrine",
"druidic-order",
"methodology",
"element",
"hybrid-study",
"mystery",
"conscious-mind",
"subconscious-mind",
"hunters-edge",
"racket",
"bloodline",
"style",
"implement",
"lesson",
"patron",
"arcane-school",
"arcane-thesis",
"way",
"innovation",
"ikon",
"epithet",
];
protected override ICollection<string> GetSchemaNodes(AbstractCompendiumItem item)
{
List<string> nodes = [item.Type];
// Нода подкласса
if (_subclassSchemaTypes.Contains(item.Type))
{
nodes.Add("_subclass");
}
return nodes;
}
}
/// <inheritdoc/>
internal class AonprdTypeResolver : TypeResolver
{
//
}
@@ -0,0 +1,41 @@
using Pf2e.Scrapper.Processors;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Scrapper.Processors.Definitions;
using Pf2e.Database;
using Pf2e.Database.Model.Common;
using Pf2e.Database.Model.Common.I18n;
using Pf2e.Database.Model.Enum;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Preparation;
[ProcessorCollection(
"aonprd:skill",
CompendiumType = ProcessorCompendiumEnum.Aonprd,
InnerType = "skill",
Description = "Навыки",
Group = ProcessorGroupEnum.AonprdPreparation
)]
[ProcessorDependencies(["aonprd:compendium"])]
internal class SkillProcessor(ProcessorInput input, Pf2eDbContext context)
: AbstractInputProcessor(input)
{
public Pf2eDbContext Context { get; } = context;
/// <summary>
/// Обучаемый объект.
/// </summary>
public Training Training = null!;
/// <inheritdoc/>
public override void Save() => Context.Trainings.Add(Training);
/// <inheritdoc/>
public override void Process()
{
Training = new()
{
Name = new() { { LocaleType.English, Input.GetRequired<string>("name") } },
Type = TrainingType.Skill,
};
}
}
@@ -0,0 +1,50 @@
using System;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2e.Scrapper.Processors;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Scrapper.Processors.Definitions;
using Pf2e.Database;
using Pf2e.Database.Model.Base.Source;
using Pf2e.Database.Model.Enum;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Preparation;
[ProcessorCollection(
"aonprd:source",
InnerType = "source",
CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Источники",
Group = ProcessorGroupEnum.AonprdPreparation
)]
[ProcessorDependencies(["aonprd:compendium"])]
internal class SourceProcessor(ProcessorInput input, AonprdBuilderGlobals globals)
: AbstractInputProcessor(input)
{
public AonprdBuilderGlobals Globals { get; } = globals;
public Pf2eDbContext Context { get; } = new();
/// <summary>
/// Источник сущности.
/// </summary>
protected Source Source = null!;
/// <inheritdoc/>
public override void Process()
{
Source = new()
{
Name = Input.GetRequired<string>("name"),
Category = Input.EnumRequired<SourceCategory>("primary_source_category"),
ReleaseDate = DateOnly.Parse(Input.GetRequired<string>("release_date")),
};
}
/// <inheritdoc/>
public override void Save()
{
Context.Sources.Add(Source);
Context.SaveChanges();
Globals.SourceIds.Add(Source.Name, Source.Id);
}
}
@@ -0,0 +1,69 @@
using System.Linq;
using System.Text.RegularExpressions;
using Pf2e.Scrapper.Helpers;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2e.Scrapper.Processors;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Database.Model.Base;
using Pf2e.Database.Model.Structure;
using Helpers_StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
using StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Subclasses;
[ProcessorCollection(
"aonprd:apparition",
InnerType = "apparition",
CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Apparitions",
Group = ProcessorGroupEnum.AonprdSubclasses
)]
[ProcessorDependencies(["aonprd:subclass-group"])]
internal partial class ApparitionProcessor(
ProcessorInput input,
Helpers_StringHelper stringHelper,
DatabaseHelper helper,
AonprdBuilderGlobals globals
) : AbstractSubclassProcessor(input, stringHelper, helper, globals)
{
/// <inheritdoc/>
public override Subclass Object { get; } = new();
/// <inheritdoc/>
public override string ClassName => "Animist";
/// <inheritdoc/>
public override string GroupName => "Apparition";
/// <inheritdoc/>
public override void ProcessObject()
{
AddSpells(Input.GetRequired<string>("spell_markdown"));
AddFocal(Entity.Description);
AddTrainings(Input.GetRequired<string>("skill_markdown"));
}
/// <summary>
/// Добавление заклинания сосуда.
/// </summary>
/// <param name="description"></param>
public void AddFocal(string description)
{
Match match = VesselSpellMarkdownRegex().Match(description);
string aonprdId = $"spell-{match.Groups[1].Value}";
Entity entity = Context
.EntityExternals.Where(x => x.ExternalId == aonprdId)
.Select(x => x.Entity)
.First();
Object.Entities.Add(entity);
}
//protected override string GetDescription() =>
// base.GetDescription()
// + AddedDescription(["skill_markdown", "spell_markdown", "summary_markdown"]);
[GeneratedRegex(@"\*\*Vessel Spell\*\* \[.*?\]\(/Spells\.aspx\?ID=(\d+)\)")]
private static partial Regex VesselSpellMarkdownRegex();
}
@@ -0,0 +1,43 @@
using System.Collections.Generic;
using System.Linq;
using Pf2e.Scrapper.Helpers;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2e.Scrapper.Processors;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Database.Model.Base;
using Pf2e.Database.Model.Structure;
using Helpers_StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
using StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Subclasses;
[ProcessorCollection(
"aonprd:arcane-school",
InnerType = "arcane-school",
CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Arcane Schools",
Group = ProcessorGroupEnum.AonprdSubclasses
)]
[ProcessorDependencies(["aonprd:subclass-group"])]
internal class ArcaneSchoolProcessor(
ProcessorInput input,
Helpers_StringHelper stringHelper,
DatabaseHelper helper,
AonprdBuilderGlobals globals
) : AbstractSubclassProcessor(input, stringHelper, helper, globals)
{
/// <inheritdoc/>
public override Subclass Object { get; } = new();
/// <inheritdoc/>
public override string ClassName => "Wizard";
/// <inheritdoc/>
public override string GroupName => "Arcane School";
/// <inheritdoc/>
public override void ProcessObject()
{
AddSpells(Input.GetRequired<string>("spell_markdown"));
}
}
@@ -0,0 +1,37 @@
using Pf2e.Scrapper.Helpers;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2e.Scrapper.Processors;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Database.Model.Structure;
using Helpers_StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
using StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Subclasses;
[ProcessorCollection(
"aonprd:arcane-thesis",
InnerType = "arcane-thesis",
CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Arcane Theses",
Group = ProcessorGroupEnum.AonprdSubclasses
)]
[ProcessorDependencies(["aonprd:subclass-group"])]
internal class ArcaneThesisProcessor(
ProcessorInput input,
Helpers_StringHelper stringHelper,
DatabaseHelper helper,
AonprdBuilderGlobals globals
) : AbstractSubclassProcessor(input, stringHelper, helper, globals)
{
/// <inheritdoc/>
public override Subclass Object { get; } = new();
/// <inheritdoc/>
public override string ClassName => "Wizard";
/// <inheritdoc/>
public override string GroupName => "Arcane Thesis";
/// <inheritdoc/>
public override void ProcessObject() { }
}
@@ -0,0 +1,42 @@
using Pf2e.Scrapper.Helpers;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2e.Scrapper.Processors;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Database.Model.Structure;
using Helpers_StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
using StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Subclasses;
[ProcessorCollection(
"aonprd:bloodline",
InnerType = "bloodline",
CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Bloodlines",
Group = ProcessorGroupEnum.AonprdSubclasses
)]
[ProcessorDependencies(["aonprd:subclass-group"])]
internal class BloodlineProcessor(
ProcessorInput input,
Helpers_StringHelper stringHelper,
DatabaseHelper helper,
AonprdBuilderGlobals globals
) : AbstractSubclassProcessor(input, stringHelper, helper, globals)
{
/// <inheritdoc/>
public override Subclass Object { get; } = new();
/// <inheritdoc/>
public override string ClassName => "Sorcerer";
/// <inheritdoc/>
public override string GroupName => "Bloodline";
/// <inheritdoc/>
public override void ProcessObject()
{
AddSpells(Input.GetRequired<string>("spell_markdown"));
Object.Extra = GenerateJsonString("tradition");
}
}
@@ -0,0 +1,40 @@
using Pf2e.Scrapper.Helpers;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2e.Scrapper.Processors;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Database.Model.Structure;
using Helpers_StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
using StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Subclasses;
[ProcessorCollection(
"aonprd:cause",
InnerType = "cause",
CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Causes",
Group = ProcessorGroupEnum.AonprdSubclasses
)]
[ProcessorDependencies(["aonprd:subclass-group"])]
internal class CauseProcessor(
ProcessorInput input,
Helpers_StringHelper stringHelper,
DatabaseHelper helper,
AonprdBuilderGlobals globals
) : AbstractSubclassProcessor(input, stringHelper, helper, globals)
{
/// <inheritdoc/>
public override Subclass Object { get; } = new();
/// <inheritdoc/>
public override string ClassName => "Champion";
/// <inheritdoc/>
public override string GroupName => "Cause";
/// <inheritdoc/>
public override void ProcessObject()
{
Object.Extra = GenerateJsonString("alignment");
}
}
@@ -0,0 +1,40 @@
using Pf2e.Scrapper.Helpers;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2e.Scrapper.Processors;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Database.Model.Structure;
using Helpers_StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
using StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Subclasses;
[ProcessorCollection(
"aonprd:conscious-mind",
InnerType = "conscious-mind",
CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Conscious Minds",
Group = ProcessorGroupEnum.AonprdSubclasses
)]
[ProcessorDependencies(["aonprd:subclass-group"])]
internal class ConsciousMindProcessor(
ProcessorInput input,
Helpers_StringHelper stringHelper,
DatabaseHelper helper,
AonprdBuilderGlobals globals
) : AbstractSubclassProcessor(input, stringHelper, helper, globals)
{
/// <inheritdoc/>
public override Subclass Object { get; } = new();
/// <inheritdoc/>
public override string ClassName => "Psychic";
/// <inheritdoc/>
public override string GroupName => "Conscious Mind";
/// <inheritdoc/>
public override void ProcessObject()
{
AddSpells(Input.GetRequired<string>("spell_markdown"));
}
}
@@ -0,0 +1,37 @@
using Pf2e.Scrapper.Helpers;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2e.Scrapper.Processors;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Database.Model.Structure;
using Helpers_StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
using StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Subclasses;
[ProcessorCollection(
"aonprd:doctrine",
InnerType = "doctrine",
CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Doctrines",
Group = ProcessorGroupEnum.AonprdSubclasses
)]
[ProcessorDependencies(["aonprd:subclass-group"])]
internal class DoctrineProcessor(
ProcessorInput input,
Helpers_StringHelper stringHelper,
DatabaseHelper helper,
AonprdBuilderGlobals globals
) : AbstractSubclassProcessor(input, stringHelper, helper, globals)
{
/// <inheritdoc/>
public override Subclass Object { get; } = new();
/// <inheritdoc/>
public override string ClassName => "Cleric";
/// <inheritdoc/>
public override string GroupName => "Doctrine";
/// <inheritdoc/>
public override void ProcessObject() { }
}
@@ -0,0 +1,37 @@
using Pf2e.Scrapper.Helpers;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2e.Scrapper.Processors;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Database.Model.Structure;
using Helpers_StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
using StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Subclasses;
[ProcessorCollection(
"aonprd:druidic-order",
InnerType = "druidic-order",
CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Druidic Orders",
Group = ProcessorGroupEnum.AonprdSubclasses
)]
[ProcessorDependencies(["aonprd:subclass-group"])]
internal class DruidicOrderProcessor(
ProcessorInput input,
Helpers_StringHelper stringHelper,
DatabaseHelper helper,
AonprdBuilderGlobals globals
) : AbstractSubclassProcessor(input, stringHelper, helper, globals)
{
/// <inheritdoc/>
public override Subclass Object { get; } = new();
/// <inheritdoc/>
public override string ClassName => "Druid";
/// <inheritdoc/>
public override string GroupName => "Druidic Order";
/// <inheritdoc/>
public override void ProcessObject() { }
}
@@ -0,0 +1,48 @@
using Pf2e.Scrapper.Helpers;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2e.Scrapper.Processors;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Database.Model.Structure;
using Helpers_StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
using StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Subclasses;
[ProcessorCollection(
"aonprd:eidolon",
InnerType = "eidolon",
CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Eidolons",
Group = ProcessorGroupEnum.AonprdSubclasses
)]
[ProcessorDependencies(["aonprd:subclass-group"])]
internal class EidolonProcessor(
ProcessorInput input,
Helpers_StringHelper stringHelper,
DatabaseHelper helper,
AonprdBuilderGlobals globals
) : AbstractSubclassProcessor(input, stringHelper, helper, globals)
{
/// <inheritdoc/>
public override Subclass Object { get; } = new();
/// <inheritdoc/>
public override string ClassName => "Summoner";
/// <inheritdoc/>
public override string GroupName => "Eidolon";
/// <inheritdoc/>
public override void ProcessObject()
{
AddTrainings(Input.GetRequired<string>("skill_markdown"));
Object.Extra = GenerateJsonString(
"alignment",
"language_markdown",
"size",
"tradition",
"speed"
);
}
}
@@ -0,0 +1,40 @@
using Pf2e.Scrapper.Helpers;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2e.Scrapper.Processors;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Database.Model.Structure;
using Helpers_StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
using StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Subclasses;
[ProcessorCollection(
"aonprd:element",
InnerType = "element",
CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Elements",
Group = ProcessorGroupEnum.AonprdSubclasses
)]
[ProcessorDependencies(["aonprd:subclass-group"])]
internal class ElementProcessor(
ProcessorInput input,
Helpers_StringHelper stringHelper,
DatabaseHelper helper,
AonprdBuilderGlobals globals
) : AbstractSubclassProcessor(input, stringHelper, helper, globals)
{
/// <inheritdoc/>
public override Subclass Object { get; } = new();
/// <inheritdoc/>
public override string ClassName => "Kineticist";
/// <inheritdoc/>
public override string GroupName => "Element";
/// <inheritdoc/>
public override void ProcessObject()
{
Object.Extra = GenerateJsonString("element");
}
}
@@ -0,0 +1,50 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Pf2e.Scrapper.Helpers;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2e.Scrapper.Processors;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Scrapper.Processors.Collection;
using Pf2e.Database;
using Pf2e.Database.Model.Base;
using Pf2e.Database.Model.Common;
using Pf2e.Database.Model.Common.I18n;
using Pf2e.Database.Model.Enum;
using Pf2e.Database.Model.Structure;
using Helpers_StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
using StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Subclasses;
[ProcessorCollection(
"aonprd:epithet",
InnerType = "epithet",
CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Epithets",
Group = ProcessorGroupEnum.AonprdSubclasses
)]
[ProcessorDependencies(["aonprd:subclass-group"])]
internal class EpithetProcessor(
ProcessorInput input,
Helpers_StringHelper stringHelper,
DatabaseHelper helper,
AonprdBuilderGlobals globals
) : AbstractSubclassProcessor(input, stringHelper, helper, globals)
{
/// <inheritdoc/>
public override Subclass Object { get; } = new();
/// <inheritdoc/>
public override string ClassName => "Exemplar";
/// <inheritdoc/>
public override string GroupName => "Epithet";
/// <inheritdoc/>
public override void ProcessObject()
{
Object.Extra = GenerateJsonString("level", "type");
// No additional processing needed
}
}
@@ -0,0 +1,42 @@
using Pf2e.Scrapper.Helpers;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2e.Scrapper.Processors;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Database.Model.Structure;
using Helpers_StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
using StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Subclasses;
// TODO: Придумать логику для обработки под-архетипа (Hellknight Order)
//[ProcessorCollection(
// "aonprd:hellknight-order",
// InnerType = "hellknight-order",
// CompendiumType = ProcessorCompendiumEnum.Aonprd,
// Description = "Hellknight Orders",
// Group = ProcessorGroupEnum.AonprdSubclasses
//)]
//[ProcessorDependencies(["aonprd:subclass-group"])]
internal class HellknightOrderProcessor(
ProcessorInput input,
Helpers_StringHelper stringHelper,
DatabaseHelper helper,
AonprdBuilderGlobals globals
) : AbstractSubclassProcessor(input, stringHelper, helper, globals)
{
/// <inheritdoc/>
public override Subclass Object { get; } = new();
/// <inheritdoc/>
public override string ClassName => "Hellknight";
/// <inheritdoc/>
public override string GroupName => "Order";
/// <inheritdoc/>
public override void ProcessObject()
{
Object.Extra = GenerateJsonString("favored_weapon_markdown");
}
}
@@ -0,0 +1,37 @@
using Pf2e.Scrapper.Helpers;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2e.Scrapper.Processors;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Database.Model.Structure;
using Helpers_StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
using StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Subclasses;
[ProcessorCollection(
"aonprd:hunters-edge",
InnerType = "hunters-edge",
CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Hunter's Edges",
Group = ProcessorGroupEnum.AonprdSubclasses
)]
[ProcessorDependencies(["aonprd:subclass-group"])]
internal class HuntersEdgeProcessor(
ProcessorInput input,
Helpers_StringHelper stringHelper,
DatabaseHelper helper,
AonprdBuilderGlobals globals
) : AbstractSubclassProcessor(input, stringHelper, helper, globals)
{
/// <inheritdoc/>
public override Subclass Object { get; } = new();
/// <inheritdoc/>
public override string ClassName => "Ranger";
/// <inheritdoc/>
public override string GroupName => "Hunter's Edge";
/// <inheritdoc/>
public override void ProcessObject() { }
}
@@ -0,0 +1,37 @@
using Pf2e.Scrapper.Helpers;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2e.Scrapper.Processors;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Database.Model.Structure;
using Helpers_StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
using StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Subclasses;
[ProcessorCollection(
"aonprd:hybrid-study",
InnerType = "hybrid-study",
CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Hybrid Studies",
Group = ProcessorGroupEnum.AonprdSubclasses
)]
[ProcessorDependencies(["aonprd:subclass-group"])]
internal class HybridStudyProcessor(
ProcessorInput input,
Helpers_StringHelper stringHelper,
DatabaseHelper helper,
AonprdBuilderGlobals globals
) : AbstractSubclassProcessor(input, stringHelper, helper, globals)
{
/// <inheritdoc/>
public override Subclass Object { get; } = new();
/// <inheritdoc/>
public override string ClassName => "Magus";
/// <inheritdoc/>
public override string GroupName => "Hybrid Study";
/// <inheritdoc/>
public override void ProcessObject() { }
}
@@ -0,0 +1,40 @@
using Pf2e.Scrapper.Helpers;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2e.Scrapper.Processors;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Database.Model.Structure;
using Helpers_StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
using StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Subclasses;
[ProcessorCollection(
"aonprd:ikon",
InnerType = "ikon",
CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Ikon",
Group = ProcessorGroupEnum.AonprdSubclasses
)]
[ProcessorDependencies(["aonprd:subclass-group"])]
internal class IkonProcessor(
ProcessorInput input,
Helpers_StringHelper stringHelper,
DatabaseHelper helper,
AonprdBuilderGlobals globals
) : AbstractSubclassProcessor(input, stringHelper, helper, globals)
{
/// <inheritdoc/>
public override Subclass Object { get; } = new();
/// <inheritdoc/>
public override string ClassName => "Exemplar";
/// <inheritdoc/>
public override string GroupName => "Ikon";
/// <inheritdoc/>
public override void ProcessObject()
{
Object.Extra = GenerateJsonString("type", "usage_markdown");
}
}
@@ -0,0 +1,37 @@
using Pf2e.Scrapper.Helpers;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2e.Scrapper.Processors;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Database.Model.Structure;
using Helpers_StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
using StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Subclasses;
[ProcessorCollection(
"aonprd:implement",
InnerType = "implement",
CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Implements",
Group = ProcessorGroupEnum.AonprdSubclasses
)]
[ProcessorDependencies(["aonprd:subclass-group"])]
internal class ImplementProcessor(
ProcessorInput input,
Helpers_StringHelper stringHelper,
DatabaseHelper helper,
AonprdBuilderGlobals globals
) : AbstractSubclassProcessor(input, stringHelper, helper, globals)
{
/// <inheritdoc/>
public override Subclass Object { get; } = new();
/// <inheritdoc/>
public override string ClassName => "Thaumaturge";
/// <inheritdoc/>
public override string GroupName => "Implement";
/// <inheritdoc/>
public override void ProcessObject() { }
}
@@ -0,0 +1,37 @@
using Pf2e.Scrapper.Helpers;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2e.Scrapper.Processors;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Database.Model.Structure;
using Helpers_StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
using StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Subclasses;
[ProcessorCollection(
"aonprd:innovation",
InnerType = "innovation",
CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Innovations",
Group = ProcessorGroupEnum.AonprdSubclasses
)]
[ProcessorDependencies(["aonprd:subclass-group"])]
internal class InnovationProcessor(
ProcessorInput input,
Helpers_StringHelper stringHelper,
DatabaseHelper helper,
AonprdBuilderGlobals globals
) : AbstractSubclassProcessor(input, stringHelper, helper, globals)
{
/// <inheritdoc/>
public override Subclass Object { get; } = new();
/// <inheritdoc/>
public override string ClassName => "Inventor";
/// <inheritdoc/>
public override string GroupName => "Innovation";
/// <inheritdoc/>
public override void ProcessObject() { }
}
@@ -0,0 +1,37 @@
using Pf2e.Scrapper.Helpers;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2e.Scrapper.Processors;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Database.Model.Structure;
using Helpers_StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
using StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Subclasses;
[ProcessorCollection(
"aonprd:instinct",
InnerType = "instinct",
CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Instincts",
Group = ProcessorGroupEnum.AonprdSubclasses
)]
[ProcessorDependencies(["aonprd:subclass-group"])]
internal class InstinctProcessor(
ProcessorInput input,
Helpers_StringHelper stringHelper,
DatabaseHelper helper,
AonprdBuilderGlobals globals
) : AbstractSubclassProcessor(input, stringHelper, helper, globals)
{
/// <inheritdoc/>
public override Subclass Object { get; } = new();
/// <inheritdoc/>
public override string ClassName => "Barbarian";
/// <inheritdoc/>
public override string GroupName => "Instinct";
/// <inheritdoc/>
public override void ProcessObject() { }
}
@@ -0,0 +1,40 @@
using Pf2e.Scrapper.Helpers;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2e.Scrapper.Processors;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Database.Model.Structure;
using Helpers_StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
using StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Subclasses;
[ProcessorCollection(
"aonprd:lesson",
InnerType = "lesson",
CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Lessons",
Group = ProcessorGroupEnum.AonprdSubclasses
)]
[ProcessorDependencies(["aonprd:subclass-group"])]
internal class LessonProcessor(
ProcessorInput input,
Helpers_StringHelper stringHelper,
DatabaseHelper helper,
AonprdBuilderGlobals globals
) : AbstractSubclassProcessor(input, stringHelper, helper, globals)
{
/// <inheritdoc/>
public override Subclass Object { get; } = new();
/// <inheritdoc/>
public override string ClassName => "Witch";
/// <inheritdoc/>
public override string GroupName => "Lesson";
/// <inheritdoc/>
public override void ProcessObject()
{
Object.Extra = GenerateJsonString("lesson_type");
}
}
@@ -0,0 +1,37 @@
using Pf2e.Scrapper.Helpers;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2e.Scrapper.Processors;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Database.Model.Structure;
using Helpers_StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
using StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Subclasses;
[ProcessorCollection(
"aonprd:methodology",
InnerType = "methodology",
CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Methodologies",
Group = ProcessorGroupEnum.AonprdSubclasses
)]
[ProcessorDependencies(["aonprd:subclass-group"])]
internal class MethodologyProcessor(
ProcessorInput input,
Helpers_StringHelper stringHelper,
DatabaseHelper helper,
AonprdBuilderGlobals globals
) : AbstractSubclassProcessor(input, stringHelper, helper, globals)
{
/// <inheritdoc/>
public override Subclass Object { get; } = new();
/// <inheritdoc/>
public override string ClassName => "Investigator";
/// <inheritdoc/>
public override string GroupName => "Methodology";
/// <inheritdoc/>
public override void ProcessObject() { }
}
@@ -0,0 +1,37 @@
using Pf2e.Scrapper.Helpers;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2e.Scrapper.Processors;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Database.Model.Structure;
using Helpers_StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
using StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Subclasses;
[ProcessorCollection(
"aonprd:muse",
InnerType = "muse",
CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Muses",
Group = ProcessorGroupEnum.AonprdSubclasses
)]
[ProcessorDependencies(["aonprd:subclass-group"])]
internal class MuseProcessor(
ProcessorInput input,
Helpers_StringHelper stringHelper,
DatabaseHelper helper,
AonprdBuilderGlobals globals
) : AbstractSubclassProcessor(input, stringHelper, helper, globals)
{
/// <inheritdoc/>
public override Subclass Object { get; } = new();
/// <inheritdoc/>
public override string ClassName => "Bard";
/// <inheritdoc/>
public override string GroupName => "Muse";
/// <inheritdoc/>
public override void ProcessObject() { }
}
@@ -0,0 +1,45 @@
using Pf2e.Scrapper.Helpers;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2e.Scrapper.Processors;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Database.Model.Structure;
using Helpers_StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
using StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Subclasses;
[ProcessorCollection(
"aonprd:mystery",
InnerType = "mystery",
CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Mysteries",
Group = ProcessorGroupEnum.AonprdSubclasses
)]
[ProcessorDependencies(["aonprd:subclass-group"])]
internal class MysteryProcessor(
ProcessorInput input,
Helpers_StringHelper stringHelper,
DatabaseHelper helper,
AonprdBuilderGlobals globals
) : AbstractSubclassProcessor(input, stringHelper, helper, globals)
{
/// <inheritdoc/>
public override Subclass Object { get; } = new();
/// <inheritdoc/>
public override string ClassName => "Oracle";
/// <inheritdoc/>
public override string GroupName => "Mystery";
/// <inheritdoc/>
public override void ProcessObject()
{
AddSpells(Input.GetRequired<string>("spell_markdown"));
// TODO: Добавть проверку на упоминание Lore в skill_markdown
// AddTrainings(Input.GetRequired<string>("skill_markdown"));
Object.Extra = GenerateJsonString("domain", "skill_markdown");
}
}
@@ -0,0 +1,51 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Pf2e.Scrapper.Helpers;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2e.Scrapper.Processors;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Scrapper.Processors.Collection;
using Pf2e.Database;
using Pf2e.Database.Model.Base;
using Pf2e.Database.Model.Common;
using Pf2e.Database.Model.Common.I18n;
using Pf2e.Database.Model.Enum;
using Pf2e.Database.Model.Structure;
using Helpers_StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
using StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Subclasses;
[ProcessorCollection(
"aonprd:patron",
InnerType = "patron",
CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Patrons",
Group = ProcessorGroupEnum.AonprdSubclasses
)]
[ProcessorDependencies(["aonprd:subclass-group"])]
internal class PatronProcessor(
ProcessorInput input,
Helpers_StringHelper stringHelper,
DatabaseHelper helper,
AonprdBuilderGlobals globals
) : AbstractSubclassProcessor(input, stringHelper, helper, globals)
{
/// <inheritdoc/>
public override Subclass Object { get; } = new();
/// <inheritdoc/>
public override string ClassName => "Witch";
/// <inheritdoc/>
public override string GroupName => "Patron";
/// <inheritdoc/>
public override void ProcessObject()
{
AddTrainings(Input.GetRequired<string>("skill_markdown"));
Object.Extra = GenerateJsonString("tradition", "skill_markdown");
}
}
@@ -0,0 +1,37 @@
using Pf2e.Scrapper.Helpers;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2e.Scrapper.Processors;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Database.Model.Structure;
using Helpers_StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
using StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Subclasses;
[ProcessorCollection(
"aonprd:practice",
InnerType = "practice",
CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Practices",
Group = ProcessorGroupEnum.AonprdSubclasses
)]
[ProcessorDependencies(["aonprd:subclass-group"])]
internal class PracticeProcessor(
ProcessorInput input,
Helpers_StringHelper stringHelper,
DatabaseHelper helper,
AonprdBuilderGlobals globals
) : AbstractSubclassProcessor(input, stringHelper, helper, globals)
{
/// <inheritdoc/>
public override Subclass Object { get; } = new();
/// <inheritdoc/>
public override string ClassName => "Animist";
/// <inheritdoc/>
public override string GroupName => "Practice";
/// <inheritdoc/>
public override void ProcessObject() { }
}
@@ -0,0 +1,37 @@
using Pf2e.Scrapper.Helpers;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2e.Scrapper.Processors;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Database.Model.Structure;
using Helpers_StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
using StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Subclasses;
[ProcessorCollection(
"aonprd:racket",
InnerType = "racket",
CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Rackets",
Group = ProcessorGroupEnum.AonprdSubclasses
)]
[ProcessorDependencies(["aonprd:subclass-group"])]
internal class RacketProcessor(
ProcessorInput input,
Helpers_StringHelper stringHelper,
DatabaseHelper helper,
AonprdBuilderGlobals globals
) : AbstractSubclassProcessor(input, stringHelper, helper, globals)
{
/// <inheritdoc/>
public override Subclass Object { get; } = new();
/// <inheritdoc/>
public override string ClassName => "Rogue";
/// <inheritdoc/>
public override string GroupName => "Racket";
/// <inheritdoc/>
public override void ProcessObject() { }
}
@@ -0,0 +1,37 @@
using Pf2e.Scrapper.Helpers;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2e.Scrapper.Processors;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Database.Model.Structure;
using Helpers_StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
using StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Subclasses;
[ProcessorCollection(
"aonprd:research-field",
InnerType = "research-field",
CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Research Fields",
Group = ProcessorGroupEnum.AonprdSubclasses
)]
[ProcessorDependencies(["aonprd:subclass-group"])]
internal class ResearchFieldProcessor(
ProcessorInput input,
Helpers_StringHelper stringHelper,
DatabaseHelper helper,
AonprdBuilderGlobals globals
) : AbstractSubclassProcessor(input, stringHelper, helper, globals)
{
/// <inheritdoc/>
public override Subclass Object { get; } = new();
/// <inheritdoc/>
public override string ClassName => "Alchemist";
/// <inheritdoc/>
public override string GroupName => "Research Field";
/// <inheritdoc/>
public override void ProcessObject() { }
}
@@ -0,0 +1,37 @@
using Pf2e.Scrapper.Helpers;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2e.Scrapper.Processors;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Database.Model.Structure;
using Helpers_StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
using StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Subclasses;
[ProcessorCollection(
"aonprd:style",
InnerType = "style",
CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Styles",
Group = ProcessorGroupEnum.AonprdSubclasses
)]
[ProcessorDependencies(["aonprd:subclass-group"])]
internal class StyleProcessor(
ProcessorInput input,
Helpers_StringHelper stringHelper,
DatabaseHelper helper,
AonprdBuilderGlobals globals
) : AbstractSubclassProcessor(input, stringHelper, helper, globals)
{
/// <inheritdoc/>
public override Subclass Object { get; } = new();
/// <inheritdoc/>
public override string ClassName => "Swashbuckler";
/// <inheritdoc/>
public override string GroupName => "Style";
/// <inheritdoc/>
public override void ProcessObject() { }
}
@@ -0,0 +1,108 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.DependencyInjection;
using Pf2e.Scrapper.Helpers;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Scrapper.Processors.Definitions;
using Pf2e.Scrapper.Processors.Factory;
using Pf2e.Database;
using Pf2e.Database.Model.Base;
using Pf2e.Database.Model.Common.I18n;
using Pf2e.Database.Model.Enum;
using Pf2e.Database.Model.Structure;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Subclasses;
[ProcessorFactory(
"aonprd:subclass-group",
ProcessorType = typeof(SubclassGroupProcessor),
Description = "Группы подклассов",
Group = ProcessorGroupEnum.AonprdSubclasses,
Parallel = true
)]
[ProcessorDependencies(
["aonprd:trait", "aonprd:source", "aonprd:class", "aonprd:spell", "aonprd:skill"]
)]
internal class SubclassGroupProcessorFactory(
IServiceProvider serviceProvider,
AonprdBuilderGlobals globals
) : AbstractProcessorFactory
{
public IServiceProvider ServiceProvider { get; } = serviceProvider;
public AonprdBuilderGlobals Globals { get; } = globals;
public override int Count() => Globals.SubclassGroups.Count;
public override IEnumerable<Tuple<string, IProcessor>> GetEnumerator()
{
foreach ((string key, string value) in Globals.SubclassGroups)
{
var processor =
value == "_null"
? ActivatorUtilities.CreateInstance<SubclassGroupProcessor>(
ServiceProvider,
key
)
: ActivatorUtilities.CreateInstance<SubclassGroupProcessor>(
ServiceProvider,
key,
value
);
yield return new($"{key} -> {value}", processor);
}
}
}
internal class SubclassGroupProcessor(string groupName, string? className, DatabaseHelper helper)
: IProcessor
{
public string GroupName { get; } = groupName;
public string? ClassName { get; } = className;
public DatabaseHelper Helper { get; } = helper;
public SubclassGroupProcessor(string groupName, DatabaseHelper helper)
: this(groupName, null, helper) { }
public Pf2eDbContext Context { get; } = new();
public void Process()
{
Class? cl = ClassName is not null
? Context.Classes.FirstOrDefault(c =>
Pf2eDbContext.LocaleSearch(c.Entity.Name, ClassName)
)
: null;
int id;
lock (Helper.IdLock)
{
id = Helper.NextEntityId;
}
Entity groupEntity = new()
{
Id = id,
Type = EntityType.SubclassGroup,
Img = "",
Description = "",
Rarity = EntityRarity.Common,
IsTranslated = false,
IsChecked = false,
Name = { [LocaleType.English] = GroupName },
Slug = Helper.GenerateSlug(GroupName),
};
Context.Entities.Add(groupEntity);
SubclassGroup group = new() { Entity = groupEntity, Class = cl };
Context.SubclassGroups.Add(group);
}
public void Save()
{
Context.SaveChanges();
}
}
@@ -0,0 +1,40 @@
using Pf2e.Scrapper.Helpers;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2e.Scrapper.Processors;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Database.Model.Structure;
using Helpers_StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
using StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Subclasses;
[ProcessorCollection(
"aonprd:subconscious-mind",
InnerType = "subconscious-mind",
CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Subconscious Minds",
Group = ProcessorGroupEnum.AonprdSubclasses
)]
[ProcessorDependencies(["aonprd:subclass-group"])]
internal class SubconsciousMindProcessor(
ProcessorInput input,
Helpers_StringHelper stringHelper,
DatabaseHelper helper,
AonprdBuilderGlobals globals
) : AbstractSubclassProcessor(input, stringHelper, helper, globals)
{
/// <inheritdoc/>
public override Subclass Object { get; } = new();
/// <inheritdoc/>
public override string ClassName => "Psychic";
/// <inheritdoc/>
public override string GroupName => "Subconscious Mind";
/// <inheritdoc/>
public override void ProcessObject()
{
Object.Extra = GenerateJsonString("attribute");
}
}
@@ -0,0 +1,37 @@
using Pf2e.Scrapper.Helpers;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2e.Scrapper.Processors;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Database.Model.Structure;
using Helpers_StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
using StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Subclasses;
[ProcessorCollection(
"aonprd:tenet",
InnerType = "tenet",
CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Tenets",
Group = ProcessorGroupEnum.AonprdSubclasses
)]
[ProcessorDependencies(["aonprd:subclass-group"])]
internal class TenetProcessor(
ProcessorInput input,
Helpers_StringHelper stringHelper,
DatabaseHelper helper,
AonprdBuilderGlobals globals
) : AbstractSubclassProcessor(input, stringHelper, helper, globals)
{
/// <inheritdoc/>
public override Subclass Object { get; } = new();
/// <inheritdoc/>
public override string ClassName => "Champion";
/// <inheritdoc/>
public override string GroupName => "Tenet";
/// <inheritdoc/>
public override void ProcessObject() { }
}
@@ -0,0 +1,37 @@
using Pf2e.Scrapper.Helpers;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2e.Scrapper.Processors;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Database.Model.Structure;
using Helpers_StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
using StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Subclasses;
[ProcessorCollection(
"aonprd:way",
InnerType = "way",
CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Ways",
Group = ProcessorGroupEnum.AonprdSubclasses
)]
[ProcessorDependencies(["aonprd:subclass-group"])]
internal class WayProcessor(
ProcessorInput input,
Helpers_StringHelper stringHelper,
DatabaseHelper helper,
AonprdBuilderGlobals globals
) : AbstractSubclassProcessor(input, stringHelper, helper, globals)
{
/// <inheritdoc/>
public override Subclass Object { get; } = new();
/// <inheritdoc/>
public override string ClassName => "Gunslinger";
/// <inheritdoc/>
public override string GroupName => "Way";
/// <inheritdoc/>
public override void ProcessObject() { }
}
@@ -0,0 +1,72 @@
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using Newtonsoft.Json.Linq;
using Pf2e.Scrapper.Helpers;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2e.Scrapper.Processors;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Database.Model.Common.I18n;
using Pf2e.Database.Model.Enum;
using Helpers_StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
using StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Types;
[ProcessorCollection(
"aonprd:action",
InnerType = "action",
CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Действия",
Group = ProcessorGroupEnum.AonprdEntities
)]
[ProcessorDependencies(["aonprd:trait", "aonprd:source"])]
internal partial class ActionProcessor(
ProcessorInput input,
Helpers_StringHelper stringHelper,
DatabaseHelper helper,
AonprdBuilderGlobals globals
) : AbstractEntityProcessor(input, stringHelper, helper, globals)
{
/// <inheritdoc/>
public override EntityType Type => EntityType.Action;
/// <inheritdoc/>
public override Database.Model.Structure.Action Object { get; } = new();
/// <inheritdoc/>
protected override void Add() => Context.Actions.Add(Object);
/// <inheritdoc/>
public override void Process()
{
Check();
base.Process();
Object.Actions = Input.EnumOptional("actions", ActionEnum.Passive);
Object.Extra = GenerateJsonString("school");
}
public override void Check()
{
base.Check();
if (Input.GetRequired<string>("markdown").StartsWith("**Activate"))
{
throw new SkipItemException(Input.GetRequired<string>("name"), "activating action");
}
}
/// <inheritdoc/>
protected override string[] AddedDescriptionPaths() =>
[
"cost_markdown",
"trait_markdown",
"source_markdown",
"search_markdown",
"requirement_markdown",
"trigger_markdown",
"summary_markdown",
];
}
@@ -0,0 +1,99 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json.Linq;
using Pf2e.Scrapper.Helpers;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2e.Scrapper.Processors;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Scrapper.Processors.Collection;
using Pf2e.Database.Model.Enum;
using Pf2e.Database.Model.Structure;
using Helpers_StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
using StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Types;
[ProcessorCollection(
"aonprd:ancestry",
InnerType = "ancestry",
CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Родословные",
Group = ProcessorGroupEnum.AonprdEntities
)]
[ProcessorDependencies(["aonprd:trait", "aonprd:source", "aonprd:language"])]
internal class AncestryProcessor(
ProcessorInput input,
Helpers_StringHelper stringHelper,
DatabaseHelper helper,
AonprdBuilderGlobals globals
) : AbstractEntityProcessor(input, stringHelper, helper, globals)
{
/// <inheritdoc/>
public override EntityType Type => EntityType.Ancestry;
/// <inheritdoc/>
public override Ancestry Object { get; } = new();
/// <inheritdoc/>
protected override void Add() => Context.Ancestries.Add(Object);
public override void Check()
{
base.Check();
if (Input.GetRequired<string>("type") != "Ancestry")
{
throw new SkipItemException(Input.GetRequired<string>("name"), "не родословная");
}
}
/// <inheritdoc/>
public override void Process()
{
base.Process();
Object.Size = Input
.GetRequired<JArray>("size")
.Select(t => Input.Enum<SizeEnum>(t.ToString()))
.ToArray();
string hp = Input.GetRequired<string>("hp_raw");
Object.Hp = Convert.ToInt32((hp != "") ? hp : "0");
Object.Speed = Convert.ToInt32(Input.GetOptional<int>("speed.land"));
Object.VisionEnum = Input.EnumOptional("vision", VisionEnum.Normal);
AddBoosts("attribute", true);
AddBoosts("attribute_flaw", false);
// TODO - import languages from Foundry!
// ProcessLanguages();
}
protected void AddBoosts(string path, bool isBoost)
{
List<string> boosts =
Input.GetOptional<JArray>(path)?.ToArray().Select(t => t.ToString()).ToList() ?? [];
if (boosts.Contains("Two free ability boosts"))
{
boosts.Remove("Two free ability boosts");
boosts.Add("Free");
boosts.Add("Free");
}
foreach (string boost in boosts)
{
Context.AncestryBoosts.Add(
new()
{
Ancestry = Object,
Boost = isBoost,
Value = Input.Enum<AttributeValues>(boost),
}
);
}
}
}
@@ -0,0 +1,48 @@
using Pf2e.Scrapper.Helpers;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2e.Scrapper.Processors;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Scrapper.Processors.Collection;
using Pf2e.Database.Model.Enum;
using Pf2e.Database.Model.Structure;
using Helpers_StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
using StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Types;
[ProcessorCollection(
"aonprd:archetype",
InnerType = "archetype",
CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Архетипы",
Group = ProcessorGroupEnum.AonprdEntities
)]
[ProcessorDependencies(["aonprd:trait", "aonprd:source"])]
internal class ArchetypeProcessor(
ProcessorInput input,
Helpers_StringHelper stringHelper,
DatabaseHelper helper,
AonprdBuilderGlobals globals
) : AbstractEntityProcessor(input, stringHelper, helper, globals)
{
/// <inheritdoc/>
public override EntityType Type => EntityType.Archetype;
/// <inheritdoc/>
public override Archetype Object { get; } = new();
/// <inheritdoc/>
protected override void Add() => Context.Archetypes.Add(Object);
/// <inheritdoc/>
public override void Process()
{
base.Process();
Object.Level = Input.GetOptional<int>("level");
}
/// <inheritdoc/>
protected override string[] AddedDescriptionPaths() =>
["prerequisite_markdown", "requirement_markdown", "summary_markdown"];
}
@@ -0,0 +1,60 @@
using Newtonsoft.Json.Linq;
using Pf2e.Scrapper.Helpers;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2e.Scrapper.Processors;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Scrapper.Processors.Collection;
using Pf2e.Database.Model.Enum;
using Pf2e.Database.Model.Structure;
using Pf2e.Database.Model.Structure.BackgroundRelation;
using Helpers_StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
using StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Types;
[ProcessorCollection(
"aonprd:background",
InnerType = "background",
CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Предыстории",
Group = ProcessorGroupEnum.AonprdEntities
)]
[ProcessorDependencies(["aonprd:trait", "aonprd:source"])]
internal class BackgroundProcessor(
ProcessorInput input,
Helpers_StringHelper stringHelper,
DatabaseHelper helper,
AonprdBuilderGlobals globals
) : AbstractEntityProcessor(input, stringHelper, helper, globals)
{
/// <inheritdoc/>
public override EntityType Type => EntityType.Background;
/// <inheritdoc/>
public override Background Object { get; } = new();
/// <inheritdoc/>
protected override void Add() => Context.Backgrounds.Add(Object);
/// <inheritdoc/>
public override void Process()
{
base.Process();
foreach (JToken token in Input.GetOptional<JArray>("attribute") ?? [])
{
BackgroundBoost boost = new()
{
Background = Object,
Attribute = Input.Enum<AttributeValues>(token.ToString()),
};
Object.BackgroundBoosts.Add(boost);
Context.BackgroundBoosts.Add(boost);
}
}
/// <inheritdoc/>
protected override string[] AddedDescriptionPaths() =>
["feat_markdown", "summary_markdown", "skill_markdown", "prerequisite_markdown"];
}
@@ -0,0 +1,261 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using Newtonsoft.Json.Linq;
using Pf2e.Scrapper.Helpers;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2e.Scrapper.Processors;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Database;
using Pf2e.Database.Model.Common;
using Pf2e.Database.Model.Common.I18n;
using Pf2e.Database.Model.Enum;
using Pf2e.Database.Model.Structure;
using Pf2e.Database.Model.Structure.ClassRelation;
using static System.Text.RegularExpressions.Regex;
using Helpers_StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
using StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Types;
[ProcessorCollection(
"aonprd:class",
InnerType = "class",
CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Классы",
Group = ProcessorGroupEnum.AonprdEntities
)]
[ProcessorDependencies(["aonprd:trait", "aonprd:source", "aonprd:skill"])]
internal partial class ClassProcessor(
ProcessorInput input,
Helpers_StringHelper stringHelper,
DatabaseHelper helper,
AonprdBuilderGlobals globals
) : AbstractEntityProcessor(input, stringHelper, helper, globals)
{
/// <inheritdoc/>
public override EntityType Type => EntityType.Class;
/// <inheritdoc/>
public override Class Object { get; } = new();
/// <inheritdoc/>
protected override void Add() => Context.Classes.Add(Object);
/// <inheritdoc/>
public override void Process()
{
base.Process();
Object.Hp = Convert.ToInt32(Input.GetRequired<string>("hp_raw"));
AttributeValues[] attributes = Input
.GetRequired<JArray>("attribute")
.Select(token => Input.Enum<AttributeValues>(token.ToString()))
.ToArray();
foreach (AttributeValues attribute in attributes)
{
ClassKeyAttribute classKeyAttribute = new() { Class = Object, Value = attribute };
Context.ClassKeyAbilities.Add(classKeyAttribute);
Object.ClassKeyAbilities.Add(classKeyAttribute);
}
ClassTraining[] classTrainings =
[
.. ProcessTraining(
Input.GetRequired<JArray>("attack_proficiency"),
TrainingType.Weapon
),
.. ProcessTraining(
Input.GetRequired<JArray>("defense_proficiency"),
TrainingType.Armor
),
.. GetSkillTrainings(),
.. GetSaveTrainings(),
];
foreach (ClassTraining classTraining in classTrainings)
{
Context.ClassTrainings.Add(classTraining);
Object.ClassTrainings.Add(classTraining);
}
JArray tradition = Input.GetOptional<JArray>("tradition") ?? [];
switch (tradition.Count)
{
case 0:
Object.Tradition = SpellTradition.None;
break;
case 1:
Object.Tradition = Input.Enum<SpellTradition>(tradition[0].ToString());
break;
default:
Object.Tradition = SpellTradition.All;
break;
}
ProcessClassTrait();
}
/// <summary>
/// Получение коллекции обучений, связанных с классом.
/// </summary>
/// <returns>Коллекция обучения.</returns>
protected ICollection<ClassTraining> GetSkillTrainings()
{
List<string> skillNames = Input
.GetRequired<JArray>("skill_proficiency")
.Select(token => token.ToString())
.ToList();
Training[] skills = Context
.Trainings.Where(t => Pf2eDbContext.LocaleSearchArray(t.Name, skillNames))
.ToArray();
return skills
.Select(skill => new ClassTraining()
{
Class = Object,
Training = skill,
Rank = TrainingRank.Trained,
})
.ToArray();
}
/// <summary>
/// Обработка массива со списком обучений.
/// </summary>
/// <param name="array">JSON-массив обучений.</param>
/// <param name="type">Тип обучения.</param>
/// <returns>Коллекция обучений.</returns>
protected ICollection<ClassTraining> ProcessTraining(JArray array, TrainingType type)
{
const string pattern = @"^(.*?) in\s+(?:the)?\s*(.*)$";
List<ClassTraining> trainings = [];
foreach (string value in array.Select(token => token.ToString()))
{
MatchCollection m = Matches(value, pattern);
if (m.Count == 0)
{
throw new($"Can't parse training string '{value}'");
}
TrainingRank rank = Input.Enum<TrainingRank>(m[0].Groups[1].Value);
foreach (string part in GetTrainingString(m[0].Groups[2].Value))
{
Training? training = Context.Trainings.FirstOrDefault(t =>
Pf2eDbContext.LocaleSearch(t.Name, part) && t.Type == type
);
if (training == null)
{
training = new()
{
Name = new() { [LocaleType.English] = part },
Type = type,
};
Context.Trainings.Add(training);
}
ClassTraining classTraining = new()
{
Class = Object,
Training = training,
Rank = rank,
};
trainings.Add(classTraining);
}
}
return trainings;
}
/// <summary>
/// Получение коллекции названий обучений из строкового представления из Архивов Нефиса.
/// </summary>
/// <param name="value">Строковое представление обучений из Архивов Нефиса.</param>
/// <returns>Коллекция названий обучений.</returns>
protected ICollection<string> 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"]
: TrainingStringSeparatorRegex()
.Split(value)
.Where(s => s != "")
.Select(s => string.Concat(s[0].ToString().ToUpper(), s.AsSpan(1)))
.ToArray();
}
/// <summary>
/// Получение CLR-представления обучений спасброскам.
/// </summary>
/// <returns>Коллекция обучений.</returns>
protected ICollection<ClassTraining> GetSaveTrainings()
{
Dictionary<string, Training> result = Context
.Trainings.Where(t => t.Type == TrainingType.Save)
.ToDictionary(t => t.Name[LocaleType.English]);
foreach (string name in new[] { "Will", "Fortitude", "Reflex", "Perception" })
{
if (result.ContainsKey(name))
{
continue;
}
Training training = new()
{
Name = new() { [LocaleType.English] = name },
Type = (name == "Perception") ? TrainingType.Perception : TrainingType.Save,
};
result.Add(name, training);
Context.Trainings.Add(training);
}
return result
.Select(pair =>
{
ClassTraining classTraining = new()
{
Class = Object,
Training = pair.Value,
Rank = Input.EnumRequired<TrainingRank>($"{pair.Key.ToLower()}_proficiency"),
};
return classTraining;
})
.ToList();
}
/// <summary>
/// Объявление соотеветсвующего классу признака классовым.
/// </summary>
protected void ProcessClassTrait()
{
Trait? classTrait = Context.Traits.FirstOrDefault(t => t.Entity.Name == Entity.Name);
if (classTrait is null)
{
return;
}
classTrait.Type = TraitType.Class;
}
/// <summary>
/// Получение регулярного выражения для разделения строки строкового представления обучений.
/// </summary>
/// <returns>Регулярное выражение.</returns>
[GeneratedRegex(@"\s*(?:,|and)\s*")]
protected static partial Regex TrainingStringSeparatorRegex();
}
@@ -0,0 +1,58 @@
using Newtonsoft.Json.Linq;
using Pf2e.Scrapper.Helpers;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2e.Scrapper.Processors;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Database;
using Pf2e.Database.Model.Common.I18n;
using Pf2e.Database.Model.Enum;
using Pf2e.Database.Model.Structure;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Types;
[ProcessorCollection(
"aonprd:deity-category",
InnerType = "deity-category",
CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Категории божеств",
Group = ProcessorGroupEnum.AonprdEntities
)]
internal class DeityCategoryProcessor(
ProcessorInput input,
StringHelper stringHelper,
DatabaseHelper helper,
AonprdBuilderGlobals globals
) : AbstractEntityProcessor(input, stringHelper, helper, globals)
{
/// <inheritdoc/>
public override EntityType Type => EntityType.DeityCategory;
/// <inheritdoc/>
public override DeityCategory Object { get; } = new();
/// <inheritdoc/>
protected override void Add() => Context.DeityCategories.Add(Object);
/// <inheritdoc/>
public override void Process()
{
base.Process();
// Обработка описания категории
ProcessDescription();
}
/// <summary>
/// Обработка описания категории.
/// </summary>
private void ProcessDescription()
{
// Получаем markdown с описанием из summary_markdown или другого поля
// В данных deity-category.yaml нет summary_markdown, используем пустую строку
string descriptionMarkdown = string.Empty;
// Преобразуем markdown в текст (можно добавить более сложную обработку)
// Пока просто сохраняем как есть
Object.Description[LocaleType.English] = descriptionMarkdown;
}
}
@@ -0,0 +1,284 @@
using System.Linq;
using System.Text.RegularExpressions;
using Newtonsoft.Json.Linq;
using Pf2e.Scrapper.Helpers;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2e.Scrapper.Processors;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Database;
using Pf2e.Database.Model.Common;
using Pf2e.Database.Model.Common.I18n;
using Pf2e.Database.Model.Enum;
using Pf2e.Database.Model.Structure;
using Pf2e.Database.Model.Structure.DeityRelation;
using Helpers_StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
using StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Types;
[ProcessorCollection(
"aonprd:deity",
InnerType = "deity",
CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Божества",
Group = ProcessorGroupEnum.AonprdEntities
)]
[ProcessorDependencies(["aonprd:domain", "aonprd:spell", "aonprd:trait", "aonprd:source"])]
internal partial class DeityProcessor(
ProcessorInput input,
Helpers_StringHelper stringHelper,
DatabaseHelper helper,
AonprdBuilderGlobals globals
) : AbstractEntityProcessor(input, stringHelper, helper, globals)
{
/// <inheritdoc/>
public override EntityType Type => EntityType.Deity;
/// <inheritdoc/>
public override Deity Object { get; } = new();
/// <inheritdoc/>
protected override void Add() => Context.Deities.Add(Object);
/// <inheritdoc/>
public override void Process()
{
base.Process();
// Установка категории божества
string categoryName = Input.GetRequired<string>("deity_category");
Object.Category = categoryName;
// Связываем с сущностью категории
ProcessCategory(categoryName);
ProcessFonts();
// Обработка атрибутов
ProcessAttributes();
// Обработка доменов
ProcessDomains();
// Обработка заклинаний
ProcessSpells();
// Обработка оружия
ProcessWeapons();
// Обработка навыков
ProcessSkills();
// Обработка текстов описания
ProcessTexts();
// Обработка освящения - в Foundry!
}
/// <inheritdoc/>
protected override void AddTraits()
{
// У божеств нет признаков. Не знаю, зачем они на Архивах.
}
/// <summary>
/// Обработка божественной силы.
/// </summary>
private void ProcessFonts()
{
JArray fonts = Input.GetOptional<JArray>("divine_font") ?? [];
switch (fonts.Count)
{
case 0:
Object.Fonts = DeityFonts.None;
return;
// Если есть и Heal, и Harm
case 2:
Object.Fonts = DeityFonts.Any;
return;
default:
{
// Определяем тип по первому элементу
string font = fonts[0].ToString();
Object.Fonts = Input.Enum<DeityFonts>(font);
break;
}
}
}
/// <summary>
/// Обработка атрибутов божества.
/// </summary>
private void ProcessAttributes()
{
JArray attributes = Input.GetOptional<JArray>("attribute") ?? [];
foreach (JToken token in attributes)
{
AttributeValues attribute = Input.Enum<AttributeValues>(token.ToString());
Object.DeityAttributes.Add(new() { Deity = Object, Attribute = attribute });
}
}
/// <summary>
/// Обработка доменов божества.
/// </summary>
private void ProcessDomains()
{
// Обработка основных доменов
string primaryDomainsMarkdown = Input.GetRequired<string>("domain_primary_markdown");
ProcessDomainMarkdown(primaryDomainsMarkdown, true);
// Обработка альтернативных доменов
string alternateDomainsMarkdown = Input.GetRequired<string>("domain_alternate_markdown");
ProcessDomainMarkdown(alternateDomainsMarkdown, false);
}
/// <summary>
/// Обработка markdown-строки с доменами.
/// </summary>
/// <param name="markdown">Markdown-строка с доменами.</param>
/// <param name="isPrimary">Флаг, указывающий, являются ли домены основными.</param>
private void ProcessDomainMarkdown(string markdown, bool isPrimary)
{
// Разделяем markdown на отдельные ссылки
MatchCollection matches = MarkdownLinkRegex().Matches(markdown);
foreach (Match match in matches)
{
string domainName = match.Groups[1].Value;
// Ищем домен в базе данных
Domain? domain = Context.Domains.FirstOrDefault(d =>
Pf2eDbContext.LocaleSearch(d.Entity.Name, domainName)
);
if (domain != null)
{
Object.DeityDomains.Add(
new()
{
Deity = Object,
Domain = domain,
IsPrimary = isPrimary,
Value = domainName,
}
);
}
}
}
/// <summary>
/// Обработка заклинаний божества.
/// </summary>
private void ProcessSpells()
{
string spellsMarkdown = Input.GetRequired<string>("spell_markdown");
MatchCollection matches = MarkdownLinkRegex().Matches(spellsMarkdown);
foreach (Match match in matches)
{
string spellName = match.Groups[1].Value;
// Ищем заклинание в базе данных
Spell? spell = Context.Spells.FirstOrDefault(s =>
Pf2eDbContext.LocaleSearch(s.Entity.Name, spellName)
);
if (spell != null)
{
Object.DeitySpells.Add(
new()
{
Deity = Object,
Spell = spell,
Level = spell.Level,
}
);
}
}
}
/// <summary>
/// Обработка оружия божества.
/// </summary>
private void ProcessWeapons()
{
string weaponsMarkdown = Input.GetRequired<string>("favored_weapon_markdown");
MatchCollection matches = MarkdownLinkRegex().Matches(weaponsMarkdown);
foreach (Match match in matches)
{
string weaponName = match.Groups[1].Value;
Object.DeityWeapons.Add(new() { Deity = Object, Weapon = weaponName });
}
}
/// <summary>
/// Обработка навыков божества.
/// </summary>
private void ProcessSkills()
{
string skillsMarkdown = Input.GetRequired<string>("skill_markdown");
MatchCollection matches = MarkdownLinkRegex().Matches(skillsMarkdown);
foreach (Match match in matches)
{
string skillName = match.Groups[1].Value;
// Ищем навык в базе данных
Training? training = Context.Trainings.FirstOrDefault(t =>
Pf2eDbContext.LocaleSearch(t.Name, skillName)
);
if (training != null)
{
Object.Skills.Add(training);
}
}
}
/// <summary>
/// Обработка категории божества.
/// </summary>
/// <param name="categoryName">Название категории.</param>
private void ProcessCategory(string categoryName)
{
// Ищем категорию в базе данных
DeityCategory? category = Context.DeityCategories.FirstOrDefault(dc =>
Pf2eDbContext.LocaleSearch(dc.Entity.Name, categoryName)
);
if (category != null)
{
Object.CategoryId = category.Id;
Object.CategoryEntity = category;
}
}
/// <summary>
/// Обработка текстов описания божества.
/// </summary>
private void ProcessTexts()
{
Object.Edicts[LocaleType.English] = Input.GetRequired<string>("edict");
Object.Anathema[LocaleType.English] = Input.GetRequired<string>("anathema");
Object.ReligiousSymbol[LocaleType.English] = Input.GetOptional<string>(
"religious_symbol",
""
);
Object.SacredAnimal[LocaleType.English] = Input.GetOptional<string>("sacred_animal", "");
Object.AreaOfConcern[LocaleType.English] = Input.GetOptional<string>(
"area_of_concern_raw",
""
);
}
/// <summary>
/// Регулярное выражение для извлечения названия из markdown-ссылки.
/// </summary>
/// <returns>Скомпилированное регулярное выражение.</returns>
[GeneratedRegex(@"\[([^\]]+)\]\([^)]+\)")]
private static partial Regex MarkdownLinkRegex();
}
@@ -0,0 +1,120 @@
using System;
using System.Linq;
using System.Text.RegularExpressions;
using Pf2e.Scrapper.Helpers;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2e.Scrapper.Processors;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Database;
using Pf2e.Database.Model.Enum;
using Pf2e.Database.Model.Structure;
using Helpers_StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
using StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Types;
[ProcessorCollection(
"aonprd:domain",
InnerType = "domain",
CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Домены",
Group = ProcessorGroupEnum.AonprdEntities
)]
[ProcessorDependencies(["aonprd:spell"])]
internal partial class DomainProcessor(
ProcessorInput input,
Helpers_StringHelper stringHelper,
DatabaseHelper helper,
AonprdBuilderGlobals globals
) : AbstractEntityProcessor(input, stringHelper, helper, globals)
{
/// <inheritdoc/>
public override EntityType Type => EntityType.Domain;
/// <inheritdoc/>
public override Domain Object { get; } = new();
/// <inheritdoc/>
protected override void Add() => Context.Domains.Add(Object);
/// <inheritdoc/>
public override void Process()
{
base.Process();
// Обработка заклинаний домена
ProcessDomainSpells();
}
/// <summary>
/// Обработка заклинаний, связанных с доменом.
/// </summary>
private void ProcessDomainSpells()
{
// Обработка основного заклинания домена
string domainSpellMarkdown = Input.GetRequired<string>("domain_spell_markdown");
Object.DomainSpellId = GetSpellIdFromMarkdown(domainSpellMarkdown);
// Обработка продвинутого заклинания домена
string advancedDomainSpellMarkdown = Input.GetRequired<string>(
"advanced_domain_spell_markdown"
);
Object.AdvancedDomainSpellId = GetSpellIdFromMarkdown(advancedDomainSpellMarkdown);
// Обработка апокрифического заклинания (если есть)
string? apocryphalSpellMarkdown = Input.GetOptional<string>("apocryphal_spell_markdown");
if (!string.IsNullOrEmpty(apocryphalSpellMarkdown))
{
Object.ApocryphalSpellId = GetSpellIdFromMarkdown(apocryphalSpellMarkdown);
}
// Обработка продвинутого апокрифического заклинания (если есть)
string? advancedApocryphalSpellMarkdown = Input.GetOptional<string>(
"advanced_apocryphal_spell_markdown"
);
if (!string.IsNullOrEmpty(advancedApocryphalSpellMarkdown))
{
Object.AdvancedApocryphalSpellId = GetSpellIdFromMarkdown(
advancedApocryphalSpellMarkdown
);
}
}
/// <summary>
/// Получение идентификатора заклинания из markdown-строки.
/// </summary>
/// <param name="markdown">Markdown-строка с информацией о заклинании.</param>
/// <returns>Идентификатор заклинания.</returns>
private int GetSpellIdFromMarkdown(string markdown)
{
// Используем регулярное выражение для извлечения названия заклинания
// Формат: [Название заклинания](/Spells.aspx?ID=XXX)
Match match = MarkdownLinkRegex().Match(markdown);
if (!match.Success)
{
throw new InvalidOperationException($"Invalid markdown format: {markdown}");
}
string spellName = match.Groups[1].Value;
// Поиск заклинания в базе данных
Spell? spell = Context.Spells.FirstOrDefault(s =>
Pf2eDbContext.LocaleSearch(s.Entity.Name, spellName)
);
if (spell == null)
{
throw new InvalidOperationException($"Spell '{spellName}' not found in database");
}
return spell.Id;
}
/// <summary>
/// Регулярное выражение для извлечения названия из markdown-ссылки.
/// </summary>
/// <returns>Скомпилированное регулярное выражение.</returns>
[GeneratedRegex(@"\[([^\]]+)\]\([^)]+\)")]
private static partial Regex MarkdownLinkRegex();
}
@@ -0,0 +1,100 @@
using System.Linq;
using Newtonsoft.Json.Linq;
using Pf2e.Scrapper.Helpers;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2e.Scrapper.Processors;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Database;
using Pf2e.Database.Model.Enum;
using Pf2e.Database.Model.Structure;
using Helpers_StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
using StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Types;
[ProcessorCollection(
"aonprd:feat",
InnerType = "feat",
CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Черты",
Group = ProcessorGroupEnum.AonprdEntities
)]
[ProcessorDependencies(["aonprd:trait", "aonprd:source", "aonprd:archetype"])]
internal class FeatProcessor(
ProcessorInput input,
Helpers_StringHelper stringHelper,
DatabaseHelper helper,
AonprdBuilderGlobals globals
) : AbstractEntityProcessor(input, stringHelper, helper, globals)
{
/// <inheritdoc/>
public override EntityType Type => EntityType.Feat;
/// <inheritdoc/>
public override Feat Object { get; } = new();
/// <inheritdoc/>
protected override void Add() => Context.Feats.Add(Object);
/// <inheritdoc/>
public override void Process()
{
base.Process();
Object.Level = Input.GetRequired<int>("level");
Object.Category = Entity.Traits.Any(t => t.Type == TraitType.Class)
? FeatCategory.Class
: FeatCategory.Uncategorized;
Object.Actions = Input.EnumOptional("actions", ActionEnum.Unknown);
Object.Extra = GenerateJsonString(
"archetype",
"element",
"heighten",
"heighten_group",
"heighten_level",
"school",
"frequency"
);
string[] archetypeStrings =
Input.GetOptional<JArray>("archetype")?.Select(t => t.ToString()).ToArray() ?? [];
Archetype[] archetypes = Context
// ReSharper disable once EntityFramework.UnsupportedServerSideFunctionCall
.Archetypes.Where(a => Pf2eDbContext.LocaleSearchArray(a.Entity.Name, archetypeStrings))
// .Where(a => archetypeStrings.Contains(a.Entity.Group.ToString()))
.ToArray();
if (archetypes.Length != 0)
{
Object.Category = FeatCategory.Archetype;
}
foreach (Archetype archetype in archetypes)
{
Object.Archetypes.Add(archetype);
if (Entity.Name.Any(l => l.Value.Contains("Dedication")))
{
archetype.DedicationFeat = Object;
}
}
}
/// <inheritdoc/>
protected override string GetDescription() =>
base.GetDescription()
+ AddedDescription(
[
"cost_markdown",
"feat_markdown",
"summary_markdown",
"prerequisite_markdown",
"requirement_markdown",
"trigger_markdown",
]
);
}
@@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Pf2e.Scrapper.Helpers;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2e.Scrapper.Processors;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Database.Model.Enum;
using Pf2e.Database.Model.Structure;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Types;
[ProcessorCollection(
"aonprd:heritage",
InnerType = "heritage",
CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Наследства",
Group = ProcessorGroupEnum.AonprdEntities
)]
[ProcessorDependencies(["aonprd:ancestry"])]
internal class HeritageProcessor(
ProcessorInput input,
StringHelper stringHelper,
DatabaseHelper helper,
AonprdBuilderGlobals globals
) : AbstractEntityProcessor(input, stringHelper, helper, globals)
{
/// <inheritdoc/>
public override EntityType Type => EntityType.Heritage;
/// <inheritdoc/>
public override Heritage Object { get; } = new();
/// <inheritdoc/>
protected override void Add() => Context.Heritages.Add(Object);
/// <inheritdoc/>
public override void Process()
{
base.Process();
}
}
@@ -0,0 +1,35 @@
using Pf2e.Scrapper.Helpers;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2e.Scrapper.Processors;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Database.Model.Enum;
using Pf2e.Database.Model.Structure;
using Helpers_StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
using StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Types;
[ProcessorCollection(
"aonprd:language",
InnerType = "language",
CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Языки",
Group = ProcessorGroupEnum.AonprdEntities
)]
[ProcessorDependencies(["aonprd:trait", "aonprd:source"])]
internal class LanguageProcessor(
ProcessorInput input,
Helpers_StringHelper stringHelper,
DatabaseHelper helper,
AonprdBuilderGlobals globals
) : AbstractEntityProcessor(input, stringHelper, helper, globals)
{
/// <inheritdoc/>
public override EntityType Type => EntityType.Language;
/// <inheritdoc/>
public override Language Object { get; } = new();
/// <inheritdoc/>
protected override void Add() => Context.Languages.Add(Object);
}
@@ -0,0 +1,408 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using Newtonsoft.Json.Linq;
using Pf2e.Scrapper.Helpers;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2e.Scrapper.Processors;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Database;
using Pf2e.Database.Model.Common.I18n;
using Pf2e.Database.Model.Enum;
using Pf2e.Database.Model.Structure;
using Pf2e.Database.Model.Structure.RitualRelation;
using Helpers_StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
using StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Types;
[ProcessorCollection(
"aonprd:ritual",
InnerType = "ritual",
CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Ритуалы",
Group = ProcessorGroupEnum.AonprdEntities
)]
[ProcessorDependencies(["aonprd:trait", "aonprd:source", "aonprd:skill"])]
internal partial class RitualProcessor(
ProcessorInput input,
Helpers_StringHelper stringHelper,
DatabaseHelper helper,
AonprdBuilderGlobals globals
) : AbstractEntityProcessor(input, stringHelper, helper, globals)
{
/// <inheritdoc/>
public override EntityType Type => EntityType.Ritual;
/// <inheritdoc/>
public override Ritual Object { get; } = new();
/// <inheritdoc/>
protected override void Add() => Context.Rituals.Add(Object);
/// <inheritdoc/>
public override void Process()
{
base.Process();
Object.Level = Input.GetRequired<int>("level");
Object.Duration = Input.GetOptional("duration", -1);
Object.Range = Input.GetOptional("range", -1);
Object.RangeString = Input.GetOptionalLocale("range_raw", LocaleType.English);
Object.CastTime = CalculateCastTime(Input.GetRequired<string>("actions"));
Object.Cost = Input.GetOptionalLocale("cost_markdown", LocaleType.English);
Object.Target = Input.GetOptionalLocale("target_markdown", LocaleType.English);
Object.Requirement = Input.GetOptionalLocale("requirement_markdown", LocaleType.English);
Object.Elements = (Input.GetOptional<JArray>("element") ?? [])
.Select(token => Input.Enum<ElementEnum>(token.ToString()))
.ToArray();
ProcessArea();
ProcessPrimaryChecks();
ProcessSecondaryChecks();
ProcessSecondaryCasters();
}
private int CalculateCastTime(string value)
{
string[] parts = value.Trim().ToLower().Split(" ");
if (parts.Length != 2)
{
throw new($"Can't determine cast time of \"{value}\"");
}
int number = int.Parse(parts[0]);
string unit = parts[1].ToLower();
int unitSeconds = unit switch
{
"hour" => 3600,
"hours" => 3600,
"day" => 86400,
"days" => 86400,
"week" => 7 * 86400,
"weeks" => 7 * 86400,
_ => throw new($"Can't determine cast time of \"{value}\""),
};
return number * unitSeconds;
}
private void ProcessArea()
{
string? areaRaw = Input.GetOptional<string>("area_raw");
if (string.IsNullOrEmpty(areaRaw))
{
return;
}
string areaType = Input.GetRequired<string[]>("area_type")[0];
RitualArea ritualArea = new()
{
Ritual = Object,
Type = Input.Enum<SpellAreaType>(areaType),
Details = new() { [LocaleType.English] = areaRaw },
};
Object.Area = ritualArea;
Context.RitualAreas.Add(ritualArea);
}
private readonly Dictionary<string, string> _castersCleanup = new()
{
[", up to a maximum of "] = "-",
[" or more"] = "-999",
[" (or more)"] = "-999",
["up to "] = "0-",
[" to "] = "-",
["at least 1"] = "1-999",
};
private void ProcessSecondaryCasters()
{
string? input = Input.GetOptional<string>("secondary_casters_raw");
if (string.IsNullOrWhiteSpace(input))
{
return;
}
foreach (KeyValuePair<string, string> pair in _castersCleanup)
{
input = input.Replace(pair.Key, pair.Value);
}
Match match = CasterRegex().Match(input);
if (!match.Success)
{
throw new($"Can't parse secondary casters: \"{input}\"");
}
Object.SecondaryCasters = int.Parse(M(1));
Object.SecondaryCastersMax = (M(2) != "") ? int.Parse(M(2)) : Object.SecondaryCasters;
if (M(3) != "")
{
Object.SecondaryCastersSuffix = new() { [LocaleType.English] = M(3) };
}
return;
string M(int i) => match.Groups[i].Value;
}
private void ProcessPrimaryChecks()
{
string? check = Input.GetOptional<string>("primary_check_markdown");
if (string.IsNullOrWhiteSpace(check))
{
return;
}
List<string> parts = CheckStringPrepare(check);
string suffix = "";
// Обработка частей
List<RitualCheck> ritualChecks = parts
.Select(part => ResolveCheck(part.Trim(), true, out suffix))
.ToList();
if (
ritualChecks.Any(c => c.Rank == TrainingRank.Untrained)
&& ritualChecks.Any(c => c.Rank != TrainingRank.Untrained)
)
{
// Смешанные ранги
TrainingRank[] ranks = ritualChecks
.Select(c => c.Rank)
.Distinct()
.Where(r => r != TrainingRank.Untrained)
.ToArray();
if (ranks.Length > 1)
{
throw new($"Can't resolve mixed ranks: {string.Join(", ", ranks)}");
}
foreach (
RitualCheck ritualCheck in ritualChecks.Where(r => r.Rank == TrainingRank.Untrained)
)
{
ritualCheck.Rank = ranks[0];
}
}
if (suffix != "")
{
Object.PrimaryCheckSuffix = new() { [LocaleType.English] = suffix };
}
foreach (RitualCheck ritualCheck in ritualChecks)
{
Object.Checks.Add(ritualCheck);
Context.RitualChecks.Add(ritualCheck);
}
}
private void ProcessSecondaryChecks()
{
string? check = Input.GetOptional<string>("secondary_check_markdown");
if (string.IsNullOrWhiteSpace(check))
{
return;
}
List<string> parts = CheckStringPrepare(check);
foreach (string part in parts)
{
RitualCheck ritualCheck = ResolveCheck(part.Trim(), false, out string suffix);
if (suffix != "")
{
if (ritualCheck.TrainingString is not null)
{
ritualCheck.TrainingString[LocaleType.English] += $" ({suffix})";
}
else
{
ritualCheck.TrainingString = new() { [LocaleType.English] = suffix };
}
}
Object.Checks.Add(ritualCheck);
Context.RitualChecks.Add(ritualCheck);
}
}
private readonly Dictionary<string, string> _checksCleanupReplaces = new()
{
["DC 35 Arcana"] = "Arcana <DC 35>",
[")or"] = ") or",
["[Lore](/Skills.aspx?ID=41) (any)"] = "[Any Lore](/Skills.aspx?ID=41)",
["[Lore](/Skills.aspx?ID=8) (any)"] = "[Any Lore](/Skills.aspx?ID=8)",
};
private readonly Dictionary<Regex, string> _checksCleanupRegexes = new()
{
[new(@"(\w+) \[Lore\]", RegexOptions.Compiled | RegexOptions.Multiline)] = "[$1 Lore]",
};
private List<string> CheckStringPrepare(string input)
{
// Отчистка проблемных частей
foreach (KeyValuePair<string, string> pair in _checksCleanupReplaces)
{
input = input.Replace(pair.Key, pair.Value);
}
foreach (KeyValuePair<Regex, string> pair in _checksCleanupRegexes)
{
input = pair.Key.Replace(input, pair.Value);
}
// Замена символа ссылок
input = Helpers_StringHelper.LinkRegex().Replace(input, "$1[$2]{$3}");
input = input.Replace("(", "<").Replace(")", ">").Replace("{", "(").Replace("}", ")");
if (input.EndsWith(';') || input.EndsWith(','))
{
input = input[..^1].Trim();
}
// Временная замена скобок ранга и дополнения
#pragma warning disable SYSLIB1045
MatchCollection rankMatches = new Regex(@"<(.*?)>").Matches(input);
#pragma warning restore SYSLIB1045
for (int i = 0; i < rankMatches.Count; i++)
{
input = input.Replace(rankMatches[i].Value, $"{{rank-{i}}}");
}
// Разбиваем строку на части
List<string> parts = StringHelper.RowSplit(input);
// Возвращаем скобки ранга и дополнения
for (int i = 0; i < rankMatches.Count; i++)
{
for (int j = 0; j < parts.Count; j++)
{
parts[j] = parts[j].Replace($"{{rank-{i}}}", rankMatches[i].Value);
}
}
return parts;
}
private RitualCheck ResolveCheck(string input, bool isPrimary, out string suffix)
{
RitualCheck result = new() { Ritual = Object, IsPrimary = isPrimary };
string? skillName = null;
string s = "";
Dictionary<Regex, Action<Match>> regexDictionary = new()
{
// General match
// [name](url) ? <rank?> ?
[PrimaryRegex1()] = match =>
{
skillName = match.Groups[1].Value;
try
{
result.Rank = Input.Enum<TrainingRank>(match.Groups[4].Value.ToLower());
s += match.Groups[3].Value + match.Groups[5].Value + match.Groups[6].Value;
}
catch
{
s +=
match.Groups[3].Value
+ match.Groups[4].Value
+ match.Groups[5].Value
+ match.Groups[6].Value;
}
},
// ? [name](url)
[PrimaryRegex2()] = match =>
{
skillName = match.Groups[2].Value;
result.Rank = TrainingRank.Untrained;
s += match.Groups[1].Value;
},
// name <rank>
[PrimaryRegex3()] = match =>
{
skillName = match.Groups[1].Value;
try
{
result.Rank = Input.Enum<TrainingRank>(match.Groups[2].Value.ToLower());
}
catch
{
s += match.Groups[2].Value;
}
},
};
foreach (KeyValuePair<Regex, Action<Match>> pair in regexDictionary)
{
Match match = pair.Key.Match(input);
if (match.Success)
{
pair.Value.Invoke(match);
break;
}
}
suffix = s;
skillName ??= input;
result.Training = Context.Trainings.FirstOrDefault(t =>
Pf2eDbContext.LocaleSearch(t.Name, skillName)
);
result.TrainingString =
(result.Training is not null) ? null : new() { [LocaleType.English] = skillName };
return result;
}
/// <inheritdoc/>
protected override string[] AddedDescriptionPaths() =>
[
"summary_markdown",
"primary_check_markdown",
"secondary_casters_raw",
"secondary_check_markdown",
];
[GeneratedRegex(
@"^ *\[\s*(.*?)\s*\]\((.*?)\)\s*([a-z' ]*?)<(.*?)(?:[;,]?\s*(.*?))?>([a-z' ]*?)$",
RegexOptions.Multiline
)]
private static partial Regex PrimaryRegex1();
[GeneratedRegex(@"^ *(.*?) *\[\s*(.*?)\s*\]\((.*?)\) *$", RegexOptions.Multiline)]
private static partial Regex PrimaryRegex2();
[GeneratedRegex(@"^ *(.*?)\s*<(.*?)> *$", RegexOptions.Multiline)]
private static partial Regex PrimaryRegex3();
[GeneratedRegex(@"^(\d+)(?:\-(\d+))?,? *\(?(.*?)\)?$", RegexOptions.Multiline)]
private static partial Regex CasterRegex();
}
@@ -0,0 +1,146 @@
using System.Linq;
using Newtonsoft.Json.Linq;
using Pf2e.Scrapper.Helpers;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2e.Scrapper.Processors;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Database.Model.Base.Source;
using Pf2e.Database.Model.Base.Targets;
using Pf2e.Database.Model.Common.I18n;
using Pf2e.Database.Model.Enum;
using Helpers_StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
using Rule = Pf2e.Database.Model.Structure.Rule;
using StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Types.Rules;
[ProcessorCollection(
"aonprd:rules",
InnerType = "rules",
CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Правила",
Group = ProcessorGroupEnum.AonprdEntities
)]
[ProcessorDependencies(["aonprd:trait", "aonprd:source", "aonprd:rules-toc"])]
internal class RuleProcessor(
ProcessorInput input,
Helpers_StringHelper stringHelper,
DatabaseHelper helper,
AonprdBuilderGlobals globals
) : AbstractEntityProcessor(input, stringHelper, helper, globals)
{
/// <inheritdoc/>
public override EntityType Type => EntityType.Rule;
/// <inheritdoc/>
public override Rule Object { get; } = new();
/// <inheritdoc/>
protected override void Add() => Context.Rules.Add(Object);
/// <summary>
/// Структура генерации содержания правил.
/// </summary>
public RuleTocCollection Toc { get; } = RuleTocCollection.Instance;
/// <inheritdoc/>
public override void Process()
{
// base.Process();
AddEntity();
AddSource();
AddExternal();
AddTraits();
RuleTocBook book = Toc[GetSourceId()];
string[] breadcrumbs = (Input.GetOptional<JArray>("breadcrumbs") ?? [])
.Select(token => token.ToString())
.ToArray();
string id = GetExternal().Suffix.Replace("/Rules.aspx?ID=", "rules-");
RuleTocItem? item;
if (breadcrumbs.Length > 0)
{
RuleTocItem parent = book.Get(breadcrumbs);
item = parent.Children.FirstOrDefault(child => child.Id == id);
if (item is null)
{
item = new(Entity.Name[LocaleType.English], parent);
parent.Children.Add(item);
}
}
else
{
item = book.Children.First(child => child.Id == id);
}
item.Id = Input.GetRequired<string>("id");
item.RuleId = Object.Id;
item.IsHandled = true;
if (item.Parent?.IsHandled ?? false)
{
Object.ParentId = item.Parent.RuleId!;
Object.Order = item.Parent.ChildrenOrder?.IndexOf(item.Id) ?? -1;
}
// ЗАМЕТКА
//
// Индекс Архивов не включает поля "next_link" и "previous_link"!
/*
if (item.ChildrenOrder is not null)
{
return;
}
item.PreviousId = Input
.GetOptional<string>("previous_link")
?.Replace("/Rules.aspx?ID=", "rules-");
item.NextId = Input.GetOptional<string>("next_link")?.Replace("/Rules.aspx?ID=", "rules-");
if (item.Parent is not null)
{
if (item.PreviousId is null)
{
item.Parent.FirstChildId = item.Id;
}
if (item.NextId is null)
{
item.Parent.LastChildId = item.Id;
}
}
*/
}
/// <summary>
/// Получение источника, к которому относится правило.
/// </summary>
/// <returns>Источник, к которому относится правило.</returns>
private int GetSourceId()
{
foreach (EntitySource entitySource in Entity.Sources)
{
return entitySource.SourceId;
}
throw new("Source not found");
}
/// <summary>
/// Получение внешней ссылки, из которой получено правило.
/// </summary>
/// <returns>Внешняя ссылка.</returns>
private EntityExternal GetExternal()
{
foreach (EntityExternal entityExternal in Entity.Externals)
{
return entityExternal;
}
throw new("External not found");
}
}
@@ -0,0 +1,186 @@
// ЗАМЕТКА
//
// В теории, этот метод должен был дополнительно устанавливать порядок разделов.
// Но индекс Архивов не включает поля "next_link" и "previous_link",
// так что ставить порядок ВСЕХ правил в архиве не получится, только тех, что есть в содержании.
// Ну а пока просто расставляем родительские и дочерние элементы.
using System.Collections.Generic;
using System.Linq;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Scrapper.Processors.Definitions;
using Pf2e.Database;
using Rule = Pf2e.Database.Model.Structure.Rule;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Types.Rules;
[Processor(
"aonprd:rules-order",
Description = "Формирование порядка правил",
Group = ProcessorGroupEnum.AonprdEntities
)]
[ProcessorDependencies(["aonprd:rules"])]
internal class RulesOrderProcessor(Pf2eDbContext context) : IProcessor
{
public Pf2eDbContext Context { get; } = context;
/// <summary>
/// Структура генерации содержания правил.
/// </summary>
public readonly RuleTocCollection Toc = RuleTocCollection.Instance;
/// <summary>
/// Список записей о порядке правил в своих главах.
/// </summary>
private readonly List<OrderItem> _orderItems = [];
/// <inheritdoc/>
public void Process()
{
Toc.SaveFile();
/*
foreach (RuleTocBook book in Toc.Books)
{
BuildIdOrderDictionary(book.Children);
}
*/
int count = Context.Rules.Count();
const int perPage = 50;
for (int skip = 0; skip < count; skip += perPage)
{
Context.Rules.Skip(skip).Take(perPage).ToList().ForEach(Process);
}
}
/// <summary>
/// Обработка конкретного правила.
/// </summary>
/// <param name="rule"></param>
private void Process(Rule rule)
{
RuleTocItem? item = Toc.FindEntityId(rule.Id);
if (item is null)
{
throw new($"Rule with id {rule.Id} not found in TOC");
}
rule.ParentId = item.Parent?.RuleId;
// Дети будут раставлены автоматически по связи
}
/// <summary>
/// Построение списка записей о порядке правил из дочерних элементов содержания.
/// </summary>
/// <param name="item">Элемент содержания, дочерние элементы которого необходимо обработать.</param>
private void BuildIdOrderDictionary(RuleTocItem item)
{
List<RuleTocItem> items = [];
if (item.ChildrenOrder is null)
{
RuleTocItem? child = item.Children.FirstOrDefault(i => i.Id == item.FirstChildId);
while (child is not null)
{
items.Add(child);
child = item.Children.FirstOrDefault(i => i.Id == child.NextId);
}
}
else
{
foreach (string id in item.ChildrenOrder)
{
RuleTocItem? child = item.Children.FirstOrDefault(i => i.Id == id);
if (child is null)
{
throw new($"Child with id {id} not found in {item.Name}");
}
items.Add(child);
}
}
if (items.Count != item.Children.Count)
{
throw new($"Children count mismatch for {item.Name}");
}
BuildIdOrderDictionary(items);
}
/// <summary>
/// Построение списка записей о порядке правил из списка элементов содержания.
/// </summary>
/// <param name="items">Список элементов содержания для обработки.</param>
private void BuildIdOrderDictionary(List<RuleTocItem> items)
{
for (int i = 0; i < items.Count; i++)
{
RuleTocItem item = items[i];
if (item.RuleId is null)
{
throw new($"RuleId is null for {item.Name} (id {item.Id}");
}
AddOrderItem((int)item.RuleId!, i, null);
BuildIdOrderDictionary(item);
}
}
/// <summary>
/// Добавление элемента содержания.
/// </summary>
/// <param name="id">Идентификатор правила.</param>
/// <param name="order">Порядок правила.</param>
/// <param name="parentId">Идентификатор главы, в которой находится глава.</param>
private void AddOrderItem(int id, int order, int? parentId)
{
if (_orderItems.Any(item => item.Id == id))
{
throw new($"Order item with id {id} already exists");
}
_orderItems.Add(
new()
{
Id = id,
Order = order,
ParentId = parentId,
}
);
}
/// <inheritdoc/>
public void Save()
{
Context.SaveChanges();
}
/// <summary>
/// Запись, указывающая порядок правила в своей главе.
/// </summary>
private record OrderItem
{
/// <summary>
/// Идентификатор правила.
/// </summary>
public int Id { get; init; }
/// <summary>
/// Порядок правила.
/// </summary>
public int Order { get; init; }
/// <summary>
/// Идентификатор главы, в которой находится глава.
/// </summary>
public int? ParentId { get; init; }
}
}
@@ -0,0 +1,157 @@
using System;
using System.Linq;
using System.Text.RegularExpressions;
using System.Web;
using AngleSharp;
using AngleSharp.Dom;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Scrapper.Processors.Definitions;
using Pf2e.Database;
using Pf2e.Database.Model.Base.Source;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Types.Rules;
[Processor(
"aonprd:rules-toc",
Description = "Загрузка содержания правил",
Group = ProcessorGroupEnum.AonprdEntities
)]
[ProcessorDependencies(["aonprd:source"])]
internal partial class RulesTocProcessor(Pf2eDbContext context) : IProcessor
{
public Pf2eDbContext Context { get; } = context;
/// <summary>
/// Селектор HTML-элемента, в котором находится содержание.
/// </summary>
private const string Selector = "#ctl00_RadDrawer1_Content_MainContent_DetailedOutput";
/// <summary>
/// Ссылка на страницу с содержанием.
/// </summary>
private const string Url = "https://2e.aonprd.com/Rules.aspx";
/// <summary>
/// Структура генерации содержания правил.
/// </summary>
public readonly RuleTocCollection Toc = RuleTocCollection.Instance;
/// <inheritdoc/>
public void Process()
{
IDocument document = DownloadPage();
foreach (IElement block in document.QuerySelector(Selector)!.Children)
{
IElement summaryElement =
block.QuerySelector("summary") ?? throw new("Can't determine <summary> element");
string sourceName = summaryElement.TextContent;
Source source =
Context.Sources.FirstOrDefault(s => s.Name == sourceName)
?? throw new($"Source {sourceName} not found");
RuleTocBook book = Toc[source];
for (int i = 1; i < block.Children.Length; i++)
{
IElement element = block.Children[i];
if (element.TagName != "B")
{
throw new($"Expected <B> tag, but got <{element.TagName}>");
}
RuleTocItem item = ParseLink(element.InnerHtml);
item.ChildrenOrder = [];
book.Children.Add(item);
if (element.NextElementSibling?.TagName != "UL")
{
continue;
}
IElement ul = block.Children[++i];
foreach (IElement li in ul.Children)
{
RuleTocItem child = ParseLink(li.InnerHtml);
item.Children.Add(child);
item.ChildrenOrder.Add(child.Id!);
}
}
}
Toc.SaveFile();
}
/// <summary>
/// Обработка ссылки в содержании для создания нового элемента содержания..
/// </summary>
/// <param name="value">Строка с ссылкой.</param>
/// <param name="parent">Родительсий элемент, к которому будет относиться новый элемент содержания.</param>
/// <returns>Новый элемент содержания.</returns>
private RuleTocItem ParseLink(string value, RuleTocItem? parent = null)
{
Match linkMatch = LinkRegex().Match(value);
if (!linkMatch.Success)
{
throw new($"Can't parse link {value}");
}
return new(HttpUtility.HtmlDecode(linkMatch.Groups[2].Value).Trim(), parent)
{
Id = $"rules-{linkMatch.Groups[1].Value}",
};
}
/// <summary>
/// Скачивание страницы, указанной в <see cref="Url"/>, и получение очищенного элемента, указанного в <see cref="Selector"/>.
/// </summary>
/// <returns></returns>
private IDocument DownloadPage()
{
IConfiguration config = Configuration.Default.WithDefaultLoader();
IBrowsingContext context = BrowsingContext.New(config);
IDocument document = null!;
bool completed = false;
while (!completed)
{
try
{
document = context.OpenAsync(Url).Result;
completed = true;
}
catch (AggregateException)
{
//
}
}
IElement root = document.QuerySelector(Selector) ?? throw new("Broken page download");
string html = root.OuterHtml;
foreach (string s in new[] { "br", "\r", "\n", "<u>", "</u>", "&amp;NoRedirect=1" })
{
html = html.Replace(s, "");
}
return context.OpenAsync(req => req.Content(html)).Result;
}
/// <inheritdoc/>
public void Save() { }
/// <summary>
/// Получить скомпилированное регулярное выражение для получения HTML-ссылки.
/// </summary>
/// <returns>Регулярное выражение получения HTML-ссылки.</returns>
[GeneratedRegex("""<a href="Rules\.aspx\?ID=(\d+)">(.*?)</a>""")]
public static partial Regex LinkRegex();
}
@@ -0,0 +1,60 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Pf2e.Scrapper.Helpers;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2e.Scrapper.Processors;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Database.Model.Enum;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Types;
[ProcessorCollection(
"aonprd:skill-general-action",
InnerType = "skill-general-action",
CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Действия скиллов",
Group = ProcessorGroupEnum.AonprdEntities
)]
[ProcessorDependencies(["aonprd:trait", "aonprd:source"])]
internal class SkillActionsProcessor(
ProcessorInput input,
StringHelper stringHelper,
DatabaseHelper helper,
AonprdBuilderGlobals globals
) : AbstractEntityProcessor(input, stringHelper, helper, globals)
{
/// <inheritdoc/>
public override EntityType Type => EntityType.Action;
/// <inheritdoc/>
public override Database.Model.Structure.Action Object { get; } = new();
/// <inheritdoc/>
protected override void Add() => Context.Actions.Add(Object);
/// <inheritdoc/>
public override void Process()
{
Check();
base.Process();
Object.Actions = ActionEnum.Other;
Object.Category = ActionCategory.SkillAction;
}
/// <inheritdoc/>
protected override string[] AddedDescriptionPaths() =>
[
"cost_markdown",
"trait_markdown",
"source_markdown",
"search_markdown",
"requirement_markdown",
"trigger_markdown",
"summary_markdown",
];
}
@@ -0,0 +1,163 @@
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json.Linq;
using Pf2e.Scrapper.Helpers;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2e.Scrapper.Processors;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Database.Model.Common.I18n;
using Pf2e.Database.Model.Enum;
using Pf2e.Database.Model.Structure;
using Pf2e.Database.Model.Structure.SpellRelation;
using Helpers_StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
using StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Types;
[ProcessorCollection(
"aonprd:spell",
InnerType = "spell",
CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Заклинания",
Group = ProcessorGroupEnum.AonprdEntities
)]
[ProcessorDependencies(["aonprd:trait", "aonprd:source"])]
internal class SpellProcessor(
ProcessorInput input,
Helpers_StringHelper stringHelper,
DatabaseHelper helper,
AonprdBuilderGlobals globals
) : AbstractEntityProcessor(input, stringHelper, helper, globals)
{
/// <inheritdoc/>
public override EntityType Type => EntityType.Spell;
/// <inheritdoc/>
public override Spell Object { get; } = new();
/// <inheritdoc/>
protected override void Add() => Context.Spells.Add(Object);
/// <inheritdoc/>
public override void Process()
{
base.Process();
Object.Level = Input.GetRequired<int>("level");
Object.Actions = Input.EnumOptional("actions", ActionEnum.Unknown);
Object.IsFocus = Input.GetRequired<string>("spell_type") == "Focus";
Object.Duration = Input.GetOptional("duration", -1);
Object.Range = Input.GetOptional("range", -1);
Object.Cost = Input.GetOptionalLocale("cost_markdown", LocaleType.English);
Object.Trigger = Input.GetOptionalLocale("trigger_markdown", LocaleType.English);
Object.Target = Input.GetOptionalLocale("target_markdown", LocaleType.English);
Object.Requirement = Input.GetOptionalLocale("requirement_markdown", LocaleType.English);
Object.Traditions = (Input.GetOptional<JArray>("tradition") ?? [])
.Select(token => Input.Enum<SpellTradition>(token.ToString()))
.ToArray();
Object.Elements = (Input.GetOptional<JArray>("element") ?? [])
.Select(token => Input.Enum<ElementEnum>(token.ToString()))
.ToArray();
switch (Input.GetRequired<string>("spell_type"))
{
case "Focus":
Object.IsFocus = true;
break;
case "Cantrip":
Object.Level = 0;
break;
}
ProcessArea();
}
/// <summary>
/// Обработка области применения заклинания.
/// </summary>
private void ProcessArea()
{
List<string> areaTypes = (Input.GetOptional<string[]>("area_type") ?? []).ToList();
switch (areaTypes.Count)
{
case 0:
return;
case 1:
A(
new()
{
Spell = Object,
Type = Input.Enum<SpellAreaType>(areaTypes[0]),
Details = Input.GetOptionalLocale("area_raw", LocaleType.English),
}
);
return;
}
List<string> areaDetails = (Input.GetOptional<string>("area_raw") ?? "")
.Replace(", or", ",")
.Replace(" or ", ", ")
.Split(", ")
.ToList();
if (areaTypes.Remove("square"))
{
A(new() { Spell = Object, Type = SpellAreaType.Square });
}
if (areaDetails.Contains("fungi"))
{
areaDetails = [areaDetails[0] + " or " + areaDetails[1]];
}
if (areaTypes.Count != areaDetails.Count)
{
throw new(
$"Area details count does not match area types count for spell {Entity.Name[LocaleType.English]}"
);
}
for (int i = 0; i < areaTypes.Count; i++)
{
A(
new()
{
Spell = Object,
Type = Input.Enum<SpellAreaType>(areaTypes[i]),
Details = new() { [LocaleType.English] = areaDetails[i] },
}
);
}
return;
void A(SpellArea spellArea)
{
Object.SpellAreas.Add(spellArea);
Context.SpellAreas.Add(spellArea);
}
}
/// <inheritdoc/>
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",
];
}
@@ -0,0 +1,88 @@
using System;
using System.Collections.Generic;
using Microsoft.Extensions.DependencyInjection;
using Pf2e.Scrapper.Helpers;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Scrapper.Processors.Definitions;
using Pf2e.Scrapper.Processors.Factory;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Subclasses;
using Pf2e.Database;
using Pf2e.Database.Model.Base;
using Pf2e.Database.Model.Enum;
using Pf2e.Database.Model.Structure;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Types;
[ProcessorFactory(
"aonprd:trait-alignments",
ProcessorType = typeof(TraitAlignmentsProcessor),
Description = "Трейты мировоззрений",
Group = ProcessorGroupEnum.AonprdPreparation,
Parallel = false
)]
internal class TraitAlignmentsProcessorFactory(IServiceProvider serviceProvider)
: AbstractProcessorFactory
{
public IServiceProvider ServiceProvider { get; } = serviceProvider;
public List<string> Alignments { get; } = ["LG", "NG", "CG", "LN", "N", "CN", "LE", "NE", "CE"];
public override int Count()
{
return Alignments.Count;
}
public override IEnumerable<Tuple<string, IProcessor>> GetEnumerator()
{
foreach (string alignment in Alignments)
{
TraitAlignmentsProcessor processor =
ActivatorUtilities.CreateInstance<TraitAlignmentsProcessor>(
ServiceProvider,
alignment
);
yield return new($"{alignment}", processor);
}
}
}
internal class TraitAlignmentsProcessor(
string alignment,
DatabaseHelper helper,
AonprdBuilderGlobals globals
) : IProcessor
{
public string Alignment { get; } = alignment;
public DatabaseHelper Helper { get; } = helper;
public AonprdBuilderGlobals Globals { get; } = globals;
public Pf2eDbContext Context { get; } = new();
public Entity Entity { get; set; } = null!;
public Trait Trait { get; set; } = null!;
public void Process()
{
Entity = new()
{
Name = new() { ["en"] = Alignment },
Type = EntityType.Trait,
Img = "",
Description = "",
Rarity = EntityRarity.Common,
IsTranslated = false,
IsChecked = false,
Slug = Helper.GenerateSlug(Alignment),
};
Trait = new() { Entity = Entity, Type = TraitType.Alignment };
}
public void Save()
{
Context.Entities.Add(Entity);
Context.Traits.Add(Trait);
Globals.TraitIds[Alignment] = Entity.Id;
}
}
@@ -0,0 +1,63 @@
using Pf2e.Scrapper.Helpers;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2e.Scrapper.Processors;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Database.Model.Common.I18n;
using Pf2e.Database.Model.Enum;
using Pf2e.Database.Model.Structure;
using Helpers_StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
using StringHelper = Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers.StringHelper;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Types;
[ProcessorCollection(
"aonprd:trait",
InnerType = "trait",
CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Признаки",
Group = ProcessorGroupEnum.AonprdEntities
)]
[ProcessorDependencies(
["aonprd:external", "aonprd:source", "aonprd:source", "aonprd:trait-alignments"]
)]
internal class TraitProcessor(
ProcessorInput input,
Helpers_StringHelper stringHelper,
DatabaseHelper helper,
AonprdBuilderGlobals globals
) : AbstractEntityProcessor(input, stringHelper, helper, globals)
{
/// <inheritdoc/>
public override EntityType Type => EntityType.Trait;
/// <inheritdoc/>
public override Trait Object { get; } = new();
/// <inheritdoc/>
protected override void Add()
{
Context.Traits.Add(Object);
Globals.TraitIds[Entity.Name[LocaleType.English]] = Entity.Id;
}
/// <inheritdoc/>
public override void Process()
{
base.Process();
Object.Type = TraitType.Regular;
Context.Traits.Add(Object);
}
/// <inheritdoc/>
protected override void AddTraits()
{
// Необходимо пропускать
}
/// <inheritdoc/>
protected override string GetDescription() =>
base.GetDescription() + AddedDescription(["summary_markdown", "search_markdown"]);
}
@@ -0,0 +1,57 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Pf2e.Scrapper.Helpers;
using Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2e.Scrapper.Processors;
using Pf2e.Scrapper.Processors.Attributes;
using Pf2e.Database.Model.Enum;
using Pf2e.Database.Model.Structure;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Types;
[ProcessorCollection(
"aonprd:versitile-heritage",
InnerType = "ancestry",
CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Универсальные наследства",
Group = ProcessorGroupEnum.AonprdEntities
)]
[ProcessorDependencies(["aonprd:ancestry"])]
internal class VersitileHeritageProcessor(
ProcessorInput input,
StringHelper stringHelper,
DatabaseHelper helper,
AonprdBuilderGlobals globals
) : AbstractEntityProcessor(input, stringHelper, helper, globals)
{
/// <inheritdoc/>
public override EntityType Type => EntityType.Heritage;
/// <inheritdoc/>
public override Heritage Object { get; } = new();
/// <inheritdoc/>
protected override void Add() => Context.Heritages.Add(Object);
public override void Check()
{
base.Check();
if (Input.GetRequired<string>("type") == "Ancestry")
{
throw new SkipItemException(
Input.GetRequired<string>("name"),
"не универсальное наследие"
);
}
}
/// <inheritdoc/>
public override void Process()
{
base.Process();
}
}