Compare commits
2
Commits
b67c964c80
...
414ac03f83
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
414ac03f83 | ||
|
|
4fec054ac6 |
+267
@@ -0,0 +1,267 @@
|
|||||||
|
# Удалите строку ниже, если вы хотите наследовать параметры .editorconfig из каталогов, расположенных выше в иерархии
|
||||||
|
root = true
|
||||||
|
|
||||||
|
# Файлы C#
|
||||||
|
[*.cs]
|
||||||
|
|
||||||
|
#### Основные параметры EditorConfig ####
|
||||||
|
|
||||||
|
# Отступы и интервалы
|
||||||
|
indent_size = 4
|
||||||
|
indent_style = space
|
||||||
|
tab_width = 4
|
||||||
|
|
||||||
|
# Предпочтения для новых строк
|
||||||
|
end_of_line = crlf
|
||||||
|
insert_final_newline = false
|
||||||
|
|
||||||
|
#### Действия кода .NET ####
|
||||||
|
|
||||||
|
# Члены типа
|
||||||
|
dotnet_hide_advanced_members = false
|
||||||
|
dotnet_member_insertion_location = with_other_members_of_the_same_kind
|
||||||
|
dotnet_property_generation_behavior = prefer_throwing_properties
|
||||||
|
|
||||||
|
# Поиск символов
|
||||||
|
dotnet_search_reference_assemblies = true
|
||||||
|
|
||||||
|
#### Рекомендации по написанию кода .NET ####
|
||||||
|
|
||||||
|
# Упорядочение Using
|
||||||
|
dotnet_separate_import_directive_groups = false
|
||||||
|
dotnet_sort_system_directives_first = false
|
||||||
|
file_header_template = unset
|
||||||
|
|
||||||
|
# Предпочтения для this. и Me.
|
||||||
|
dotnet_style_qualification_for_event = false
|
||||||
|
dotnet_style_qualification_for_field = false
|
||||||
|
dotnet_style_qualification_for_method = false
|
||||||
|
dotnet_style_qualification_for_property = false
|
||||||
|
|
||||||
|
# Параметры использования ключевых слов языка и типов BCL
|
||||||
|
dotnet_style_predefined_type_for_locals_parameters_members = true
|
||||||
|
dotnet_style_predefined_type_for_member_access = true
|
||||||
|
|
||||||
|
# Предпочтения для скобок
|
||||||
|
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity
|
||||||
|
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity
|
||||||
|
dotnet_style_parentheses_in_other_operators = never_if_unnecessary
|
||||||
|
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity
|
||||||
|
|
||||||
|
# Предпочтения модификатора
|
||||||
|
dotnet_style_require_accessibility_modifiers = for_non_interface_members
|
||||||
|
|
||||||
|
# Выражения уровень предпочтения
|
||||||
|
dotnet_prefer_system_hash_code = true
|
||||||
|
dotnet_style_coalesce_expression = true
|
||||||
|
dotnet_style_collection_initializer = true
|
||||||
|
dotnet_style_explicit_tuple_names = true
|
||||||
|
dotnet_style_namespace_match_folder = true
|
||||||
|
dotnet_style_null_propagation = true
|
||||||
|
dotnet_style_object_initializer = true
|
||||||
|
dotnet_style_operator_placement_when_wrapping = beginning_of_line
|
||||||
|
dotnet_style_prefer_auto_properties = true
|
||||||
|
dotnet_style_prefer_collection_expression = when_types_loosely_match
|
||||||
|
dotnet_style_prefer_compound_assignment = true
|
||||||
|
dotnet_style_prefer_conditional_expression_over_assignment = true
|
||||||
|
dotnet_style_prefer_conditional_expression_over_return = true
|
||||||
|
dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed
|
||||||
|
dotnet_style_prefer_inferred_anonymous_type_member_names = true
|
||||||
|
dotnet_style_prefer_inferred_tuple_names = true
|
||||||
|
dotnet_style_prefer_is_null_check_over_reference_equality_method = true
|
||||||
|
dotnet_style_prefer_simplified_boolean_expressions = true
|
||||||
|
dotnet_style_prefer_simplified_interpolation = true
|
||||||
|
|
||||||
|
# Предпочтения для полей
|
||||||
|
dotnet_style_readonly_field = true
|
||||||
|
|
||||||
|
# Настройки параметров
|
||||||
|
dotnet_code_quality_unused_parameters = all
|
||||||
|
|
||||||
|
# Параметры подавления
|
||||||
|
dotnet_remove_unnecessary_suppression_exclusions = none
|
||||||
|
|
||||||
|
# Предпочтения для новых строк
|
||||||
|
dotnet_style_allow_multiple_blank_lines_experimental = true
|
||||||
|
dotnet_style_allow_statement_immediately_after_block_experimental = true
|
||||||
|
|
||||||
|
#### Рекомендации по написанию кода C# ####
|
||||||
|
|
||||||
|
# Предпочтения var
|
||||||
|
csharp_style_var_elsewhere = false
|
||||||
|
csharp_style_var_for_built_in_types = false
|
||||||
|
csharp_style_var_when_type_is_apparent = false
|
||||||
|
|
||||||
|
# Члены, заданные выражениями
|
||||||
|
csharp_style_expression_bodied_accessors = true
|
||||||
|
csharp_style_expression_bodied_constructors = false
|
||||||
|
csharp_style_expression_bodied_indexers = true
|
||||||
|
csharp_style_expression_bodied_lambdas = true
|
||||||
|
csharp_style_expression_bodied_local_functions = false
|
||||||
|
csharp_style_expression_bodied_methods = false
|
||||||
|
csharp_style_expression_bodied_operators = false
|
||||||
|
csharp_style_expression_bodied_properties = true
|
||||||
|
|
||||||
|
# Настройки соответствия шаблонов
|
||||||
|
csharp_style_pattern_matching_over_as_with_null_check = true
|
||||||
|
csharp_style_pattern_matching_over_is_with_cast_check = true
|
||||||
|
csharp_style_prefer_extended_property_pattern = true
|
||||||
|
csharp_style_prefer_not_pattern = true
|
||||||
|
csharp_style_prefer_pattern_matching = true
|
||||||
|
csharp_style_prefer_switch_expression = true
|
||||||
|
|
||||||
|
# Настройки проверки на null
|
||||||
|
csharp_style_conditional_delegate_call = true
|
||||||
|
|
||||||
|
# Предпочтения модификатора
|
||||||
|
csharp_prefer_static_anonymous_function = true
|
||||||
|
csharp_prefer_static_local_function = true
|
||||||
|
csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async
|
||||||
|
csharp_style_prefer_readonly_struct = true
|
||||||
|
csharp_style_prefer_readonly_struct_member = true
|
||||||
|
|
||||||
|
# Предпочтения для блоков кода
|
||||||
|
csharp_prefer_braces = true
|
||||||
|
csharp_prefer_simple_using_statement = true
|
||||||
|
csharp_prefer_system_threading_lock = true
|
||||||
|
csharp_style_namespace_declarations = file_scoped
|
||||||
|
csharp_style_prefer_method_group_conversion = true
|
||||||
|
csharp_style_prefer_primary_constructors = true
|
||||||
|
csharp_style_prefer_top_level_statements = true
|
||||||
|
|
||||||
|
# Выражения уровень предпочтения
|
||||||
|
csharp_prefer_simple_default_expression = true:suggestion
|
||||||
|
csharp_style_deconstructed_variable_declaration = true:suggestion
|
||||||
|
csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion
|
||||||
|
csharp_style_inlined_variable_declaration = true:suggestion
|
||||||
|
csharp_style_prefer_index_operator = true:suggestion
|
||||||
|
csharp_style_prefer_local_over_anonymous_function = true:suggestion
|
||||||
|
csharp_style_prefer_null_check_over_type_check = true:suggestion
|
||||||
|
csharp_style_prefer_range_operator = true:suggestion
|
||||||
|
csharp_style_prefer_tuple_swap = true:suggestion
|
||||||
|
csharp_style_prefer_unbound_generic_type_in_nameof = true:suggestion
|
||||||
|
csharp_style_prefer_utf8_string_literals = true:suggestion
|
||||||
|
csharp_style_throw_expression = true:suggestion
|
||||||
|
csharp_style_unused_value_assignment_preference = discard_variable:suggestion
|
||||||
|
csharp_style_unused_value_expression_statement_preference = discard_variable:silent
|
||||||
|
|
||||||
|
# предпочтения для директивы using
|
||||||
|
csharp_using_directive_placement = outside_namespace
|
||||||
|
|
||||||
|
# Предпочтения для новых строк
|
||||||
|
csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true
|
||||||
|
csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true
|
||||||
|
csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true
|
||||||
|
csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true
|
||||||
|
csharp_style_allow_embedded_statements_on_same_line_experimental = true
|
||||||
|
|
||||||
|
#### Правила форматирования C# ####
|
||||||
|
|
||||||
|
# Предпочтения для новых строк
|
||||||
|
csharp_new_line_before_catch = true
|
||||||
|
csharp_new_line_before_else = true
|
||||||
|
csharp_new_line_before_finally = true
|
||||||
|
csharp_new_line_before_members_in_anonymous_types = true
|
||||||
|
csharp_new_line_before_members_in_object_initializers = true
|
||||||
|
csharp_new_line_before_open_brace = all
|
||||||
|
csharp_new_line_between_query_expression_clauses = true
|
||||||
|
|
||||||
|
# Параметры отступов
|
||||||
|
csharp_indent_block_contents = true
|
||||||
|
csharp_indent_braces = false
|
||||||
|
csharp_indent_case_contents = true
|
||||||
|
csharp_indent_case_contents_when_block = false
|
||||||
|
csharp_indent_labels = one_less_than_current
|
||||||
|
csharp_indent_switch_labels = true
|
||||||
|
|
||||||
|
# Предпочтения для интервалов
|
||||||
|
csharp_space_after_cast = true
|
||||||
|
csharp_space_after_colon_in_inheritance_clause = true
|
||||||
|
csharp_space_after_comma = true
|
||||||
|
csharp_space_after_dot = false
|
||||||
|
csharp_space_after_keywords_in_control_flow_statements = true
|
||||||
|
csharp_space_after_semicolon_in_for_statement = true
|
||||||
|
csharp_space_around_binary_operators = before_and_after
|
||||||
|
csharp_space_around_declaration_statements = false
|
||||||
|
csharp_space_before_colon_in_inheritance_clause = true
|
||||||
|
csharp_space_before_comma = false
|
||||||
|
csharp_space_before_dot = false
|
||||||
|
csharp_space_before_open_square_brackets = false
|
||||||
|
csharp_space_before_semicolon_in_for_statement = false
|
||||||
|
csharp_space_between_empty_square_brackets = false
|
||||||
|
csharp_space_between_method_call_empty_parameter_list_parentheses = false
|
||||||
|
csharp_space_between_method_call_name_and_opening_parenthesis = false
|
||||||
|
csharp_space_between_method_call_parameter_list_parentheses = false
|
||||||
|
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
|
||||||
|
csharp_space_between_method_declaration_name_and_open_parenthesis = false
|
||||||
|
csharp_space_between_method_declaration_parameter_list_parentheses = false
|
||||||
|
csharp_space_between_parentheses = false
|
||||||
|
csharp_space_between_square_brackets = false
|
||||||
|
|
||||||
|
# Параметры переноса
|
||||||
|
csharp_preserve_single_line_blocks = true
|
||||||
|
csharp_preserve_single_line_statements = false
|
||||||
|
|
||||||
|
#### Стили именования ####
|
||||||
|
|
||||||
|
# Правила именования
|
||||||
|
|
||||||
|
dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
|
||||||
|
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
|
||||||
|
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
|
||||||
|
|
||||||
|
dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
|
||||||
|
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
|
||||||
|
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
|
||||||
|
|
||||||
|
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
|
||||||
|
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
|
||||||
|
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
|
||||||
|
|
||||||
|
# Спецификации символов
|
||||||
|
|
||||||
|
dotnet_naming_symbols.interface.applicable_kinds = interface
|
||||||
|
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||||
|
dotnet_naming_symbols.interface.required_modifiers =
|
||||||
|
|
||||||
|
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
|
||||||
|
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||||
|
dotnet_naming_symbols.types.required_modifiers =
|
||||||
|
|
||||||
|
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
|
||||||
|
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||||
|
dotnet_naming_symbols.non_field_members.required_modifiers =
|
||||||
|
|
||||||
|
# Стили именования
|
||||||
|
|
||||||
|
dotnet_naming_style.pascal_case.required_prefix =
|
||||||
|
dotnet_naming_style.pascal_case.required_suffix =
|
||||||
|
dotnet_naming_style.pascal_case.word_separator =
|
||||||
|
dotnet_naming_style.pascal_case.capitalization = pascal_case
|
||||||
|
|
||||||
|
dotnet_naming_style.begins_with_i.required_prefix = I
|
||||||
|
dotnet_naming_style.begins_with_i.required_suffix =
|
||||||
|
dotnet_naming_style.begins_with_i.word_separator =
|
||||||
|
dotnet_naming_style.begins_with_i.capitalization = pascal_case
|
||||||
|
|
||||||
|
[*.{cs,vb}]
|
||||||
|
dotnet_style_coalesce_expression = true:suggestion
|
||||||
|
dotnet_style_null_propagation = true:suggestion
|
||||||
|
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
|
||||||
|
dotnet_style_prefer_auto_properties = true:silent
|
||||||
|
dotnet_style_object_initializer = true:suggestion
|
||||||
|
dotnet_style_collection_initializer = true:suggestion
|
||||||
|
dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
|
||||||
|
dotnet_style_prefer_conditional_expression_over_assignment = true:silent
|
||||||
|
dotnet_style_prefer_conditional_expression_over_return = true:silent
|
||||||
|
dotnet_style_explicit_tuple_names = true:suggestion
|
||||||
|
dotnet_style_prefer_inferred_tuple_names = true:suggestion
|
||||||
|
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
|
||||||
|
dotnet_style_prefer_compound_assignment = true:suggestion
|
||||||
|
dotnet_style_prefer_simplified_interpolation = true:suggestion
|
||||||
|
dotnet_style_prefer_collection_expression = when_types_loosely_match:suggestion
|
||||||
|
dotnet_style_namespace_match_folder = true:suggestion
|
||||||
|
dotnet_code_quality_unused_parameters = all:suggestion
|
||||||
|
dotnet_style_operator_placement_when_wrapping = beginning_of_line
|
||||||
|
tab_width = 4
|
||||||
|
indent_size = 4
|
||||||
@@ -1,41 +1,40 @@
|
|||||||
using dotenv.net;
|
using dotenv.net;
|
||||||
using dotenv.net.Utilities;
|
using dotenv.net.Utilities;
|
||||||
|
|
||||||
namespace Pf2eDataScrapper
|
namespace Pf2eDataScrapper;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Класс настроек.
|
||||||
|
/// </summary>
|
||||||
|
internal static class EnvSettings
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Класс настроек.
|
/// Загрузка файла настроек `.env`.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static class EnvSettings
|
public static void Load()
|
||||||
{
|
{
|
||||||
/// <summary>
|
DotEnv.Load();
|
||||||
/// Загрузка файла настроек `.env`.
|
|
||||||
/// </summary>
|
|
||||||
public static void Load()
|
|
||||||
{
|
|
||||||
DotEnv.Load();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Путь к папке с входными данными.
|
|
||||||
/// </summary>
|
|
||||||
public static string DataDirPath => EnvReader.GetStringValue("DATA_DIR");
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Папка в входными данными.
|
|
||||||
/// </summary>
|
|
||||||
public static DirectoryInfo DataDir => new(DataDirPath);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Путь к папке для сохранения результатов.
|
|
||||||
/// </summary>
|
|
||||||
public static string OutputDirPath => EnvReader.GetStringValue("OUTPUT_DIR");
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Папка для сохранения результатов.
|
|
||||||
/// </summary>
|
|
||||||
public static string OutputDir => new(OutputDirPath);
|
|
||||||
|
|
||||||
public static string DatabaseConnection => EnvReader.GetStringValue("DATABASE_CONNECTION");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Путь к папке с входными данными.
|
||||||
|
/// </summary>
|
||||||
|
public static string DataDirPath => EnvReader.GetStringValue("DATA_DIR");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Папка в входными данными.
|
||||||
|
/// </summary>
|
||||||
|
public static DirectoryInfo DataDir => new(DataDirPath);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Путь к папке для сохранения результатов.
|
||||||
|
/// </summary>
|
||||||
|
public static string OutputDirPath => EnvReader.GetStringValue("OUTPUT_DIR");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Папка для сохранения результатов.
|
||||||
|
/// </summary>
|
||||||
|
public static string OutputDir => new(OutputDirPath);
|
||||||
|
|
||||||
|
public static string DatabaseConnection => EnvReader.GetStringValue("DATABASE_CONNECTION");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,42 +1,41 @@
|
|||||||
using Pf2eDataScrapper.Importers.Repositories;
|
using Pf2eDataScrapper.Importers.Repositories;
|
||||||
|
|
||||||
namespace Pf2eDataScrapper.Importers
|
namespace Pf2eDataScrapper.Importers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Абстрактный класс импортёра
|
||||||
|
/// </summary>
|
||||||
|
internal abstract class AbstractImporter
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Абстрактный класс импортёра
|
/// Отображаемое имя импортёра
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal abstract class AbstractImporter
|
public abstract string Name { get; }
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Отображаемое имя импортёра
|
|
||||||
/// </summary>
|
|
||||||
public abstract string Name { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Короткое имя импортёра
|
/// Короткое имя импортёра
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract string Slug { get; }
|
public abstract string Slug { get; }
|
||||||
|
|
||||||
public abstract AbstractImporterRepository Repository { get; }
|
public abstract AbstractImporterRepository Repository { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Путь к директории с данными для импортёра
|
/// Путь к директории с данными для импортёра
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Dir => Path.Combine(EnvSettings.DataDirPath, Slug);
|
public string Dir => Path.Combine(EnvSettings.DataDirPath, Slug);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Метод подготовки данных для импорта
|
/// Метод подготовки данных для импорта
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract void Prepare();
|
public abstract void Prepare();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Импорт данных
|
/// Импорт данных
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract void Import();
|
public abstract void Import();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Построение схем данных
|
/// Построение схем данных
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract void BuildSchemas();
|
public abstract void BuildSchemas();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,40 +1,39 @@
|
|||||||
using Pf2eDataScrapper.Importers.Repositories;
|
using Pf2eDataScrapper.Importers.Implementations.Aonprd.SchemaBuilder;
|
||||||
using Pf2eDataScrapper.Importers.Implementations.Aonprd.SchemaBuilder;
|
using Pf2eDataScrapper.Importers.Repositories;
|
||||||
|
|
||||||
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd
|
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd;
|
||||||
|
|
||||||
|
internal class AonprdImporter : AbstractImporter
|
||||||
{
|
{
|
||||||
internal class AonprdImporter : AbstractImporter
|
public override string Name => "Archives of Nethys Pathfinder 2e";
|
||||||
|
|
||||||
|
public override string Slug => "aonprd";
|
||||||
|
|
||||||
|
public override AbstractImporterRepository Repository => new AonprdRepository(this, "https://elasticsearch.aonprd.com");
|
||||||
|
|
||||||
|
public AonprdLoader Loader { get; }
|
||||||
|
|
||||||
|
public AonprdImporter()
|
||||||
{
|
{
|
||||||
public override string Name => "Archives of Nethys Pathfinder 2e";
|
Loader = new AonprdLoader(this);
|
||||||
|
}
|
||||||
|
|
||||||
public override string Slug => "aonprd";
|
public override void Prepare()
|
||||||
|
{
|
||||||
|
Repository.Update();
|
||||||
|
Loader.Prepare();
|
||||||
|
}
|
||||||
|
|
||||||
public override AbstractImporterRepository Repository => new AonprdRepository(this, "https://elasticsearch.aonprd.com");
|
public override void BuildSchemas()
|
||||||
|
{
|
||||||
|
AonprdSchemaBuilder builder = new(Loader);
|
||||||
|
|
||||||
public AonprdLoader Loader { get; }
|
builder.BuildSchemas();
|
||||||
|
builder.Save(EnvSettings.OutputDir);
|
||||||
|
}
|
||||||
|
|
||||||
public AonprdImporter()
|
public override void Import()
|
||||||
{
|
{
|
||||||
Loader = new AonprdLoader(this);
|
throw new NotImplementedException();
|
||||||
}
|
|
||||||
|
|
||||||
public override void Prepare()
|
|
||||||
{
|
|
||||||
Repository.Update();
|
|
||||||
Loader.Prepare();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void BuildSchemas()
|
|
||||||
{
|
|
||||||
var builder = new AonprdSchemaBuilder(Loader);
|
|
||||||
|
|
||||||
builder.BuildSchemas();
|
|
||||||
builder.Save(EnvSettings.OutputDir);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Import()
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,79 +1,77 @@
|
|||||||
using Microsoft.EntityFrameworkCore.Metadata.Internal;
|
using Newtonsoft.Json.Linq;
|
||||||
using Newtonsoft.Json.Linq;
|
|
||||||
using Pf2eDataScrapper.Importers.Implementations.Aonprd.Compendium;
|
using Pf2eDataScrapper.Importers.Implementations.Aonprd.Compendium;
|
||||||
|
|
||||||
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd
|
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd;
|
||||||
|
|
||||||
|
internal class AonprdLoader(AonprdImporter importer)
|
||||||
{
|
{
|
||||||
internal class AonprdLoader (AonprdImporter importer)
|
public AonprdImporter Importer = importer;
|
||||||
|
|
||||||
|
public AonprdCompendium Compendium = new();
|
||||||
|
|
||||||
|
public int ItemCount => Compendium.Count;
|
||||||
|
|
||||||
|
public void Prepare()
|
||||||
{
|
{
|
||||||
public AonprdImporter Importer = importer;
|
using StreamReader indexFile = new(Path.Combine(Importer.Dir, "index.json"));
|
||||||
|
JObject indexJson = JObject.Parse(indexFile.ReadToEnd());
|
||||||
|
|
||||||
public AonprdCompendium Compendium = new();
|
foreach (KeyValuePair<string, JToken?> kvp in indexJson)
|
||||||
|
|
||||||
public int ItemCount => Compendium.Count;
|
|
||||||
|
|
||||||
public void Prepare()
|
|
||||||
{
|
{
|
||||||
using var indexFile = new StreamReader(Path.Combine(Importer.Dir, "index.json"));
|
string file = kvp.Key.ToString();
|
||||||
var indexJson = JObject.Parse(indexFile.ReadToEnd());
|
JArray items = (JArray) kvp.Value!;
|
||||||
|
|
||||||
foreach (var kvp in indexJson)
|
foreach (JToken item in items)
|
||||||
{
|
{
|
||||||
var file = kvp.Key.ToString();
|
Compendium.AddReference(item.ToString(), file);
|
||||||
var items = (JArray)kvp.Value!;
|
|
||||||
|
|
||||||
foreach (var item in items)
|
|
||||||
{
|
|
||||||
Compendium.AddReference(item.ToString(), file);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public IEnumerable<LoaderEntry> Entries()
|
public IEnumerable<LoaderEntry> Entries()
|
||||||
{
|
{
|
||||||
return Entries(Compendium.Types.Keys.ToArray());
|
return Entries([.. Compendium.Types.Keys]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<LoaderEntry> Entries(string types)
|
public IEnumerable<LoaderEntry> Entries(string types)
|
||||||
{
|
{
|
||||||
return Entries([types]);
|
return Entries([types]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<LoaderEntry> Entries(string[] types)
|
public IEnumerable<LoaderEntry> Entries(string[] types)
|
||||||
|
{
|
||||||
|
foreach (string? file in Compendium.Files(types))
|
||||||
{
|
{
|
||||||
foreach (var file in Compendium.Files(types))
|
if (file is null)
|
||||||
{
|
{
|
||||||
if (file is null)
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
using StreamReader fileReader = new(Path.Combine(Importer.Dir, $"{file}.json"));
|
||||||
|
JArray array = JArray.Parse(fileReader.ReadToEnd());
|
||||||
|
|
||||||
|
foreach (JToken item in array)
|
||||||
|
{
|
||||||
|
string? id = item.SelectToken("id")?.ToString();
|
||||||
|
|
||||||
|
if (id is null)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
using var fileReader = new StreamReader(Path.Combine(Importer.Dir, $"{file}.json"));
|
LoaderEntry entry = new(id, file, (JObject) item);
|
||||||
var array = JArray.Parse(fileReader.ReadToEnd());
|
|
||||||
|
|
||||||
foreach (var item in array)
|
if (types.Contains(entry.Type))
|
||||||
{
|
{
|
||||||
var id = item.SelectToken("id")?.ToString();
|
yield return entry;
|
||||||
|
|
||||||
if (id is null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var entry = new LoaderEntry(id, file, (JObject)item);
|
|
||||||
|
|
||||||
if (types.Contains(entry.Type))
|
|
||||||
{
|
|
||||||
yield return entry;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public class LoaderEntry(string id, string file, JObject itemJson) : AonprdReference(id, file)
|
public class LoaderEntry(string id, string file, JObject itemJson) : AonprdReference(id, file)
|
||||||
{
|
{
|
||||||
public JObject Json { get; } = itemJson;
|
public JObject Json { get; } = itemJson;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,101 +2,100 @@
|
|||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using Pf2eDataScrapper.Importers.Repositories;
|
using Pf2eDataScrapper.Importers.Repositories;
|
||||||
|
|
||||||
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd
|
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd;
|
||||||
|
|
||||||
|
internal class AonprdRepository(AbstractImporter importer, string searchUrl) : AbstractImporterRepository(importer)
|
||||||
{
|
{
|
||||||
internal class AonprdRepository(AbstractImporter importer, string searchUrl): AbstractImporterRepository(importer)
|
public readonly string SearchUrl = searchUrl;
|
||||||
|
|
||||||
|
public int Revision { get; private set; }
|
||||||
|
|
||||||
|
private string Load(string url)
|
||||||
{
|
{
|
||||||
public readonly string SearchUrl = searchUrl;
|
if (!url.StartsWith("http://") && !url.StartsWith("https://"))
|
||||||
|
|
||||||
public int Revision { get; private set; }
|
|
||||||
|
|
||||||
private string Load(string url)
|
|
||||||
{
|
{
|
||||||
if (!url.StartsWith("http://") && !url.StartsWith("https://"))
|
url = SearchUrl + url;
|
||||||
{
|
}
|
||||||
url = SearchUrl + url;
|
|
||||||
}
|
|
||||||
|
|
||||||
using var client = new HttpClient();
|
using HttpClient client = new();
|
||||||
using var stream = client.GetStreamAsync(url);
|
using Task<Stream> stream = client.GetStreamAsync(url);
|
||||||
using var reader = new StreamReader(stream.Result);
|
using StreamReader reader = new(stream.Result);
|
||||||
|
|
||||||
return reader.ReadToEnd();
|
return reader.ReadToEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public override void Update()
|
||||||
|
{
|
||||||
|
if (CheckRevision())
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Данные Aonprd актуальны, текущая ревизия {Revision}.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine($"Данные Aonprd устарели, обновляем {RepositoryDirPath} до ревизии {Revision}...");
|
||||||
|
|
||||||
|
if (Directory.Exists(RepositoryDirPath))
|
||||||
|
{
|
||||||
|
Directory.Delete(RepositoryDirPath, true);
|
||||||
|
}
|
||||||
|
Directory.CreateDirectory(RepositoryDirPath);
|
||||||
|
|
||||||
|
WriteFile("revision.txt", Revision.ToString());
|
||||||
|
|
||||||
|
Console.WriteLine("Загрузка индекса...");
|
||||||
|
string indexContent = Load($"/json-data/aon{Revision}-index.json");
|
||||||
|
WriteFile("index.json", indexContent);
|
||||||
|
|
||||||
|
JObject indexJson = JObject.Parse(indexContent);
|
||||||
|
|
||||||
|
Console.WriteLine("Загрузка индексных файлов...");
|
||||||
|
|
||||||
|
ProgressBar bar = new(indexJson.Count);
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
foreach (KeyValuePair<string, JToken?> kvp in indexJson)
|
||||||
|
{
|
||||||
|
string fileName = kvp.Key;
|
||||||
|
bar.Refresh(i++, fileName);
|
||||||
|
|
||||||
|
string fileContent = Load($"/json-data/{fileName}.json");
|
||||||
|
WriteFile($"{fileName}.json", fileContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public override void Update()
|
bar.Refresh(indexJson.Count, "Готово!");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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 Exception("Can't get revision"));
|
||||||
|
|
||||||
|
string revisionFile = Path.Combine(RepositoryDirPath, "revision.txt");
|
||||||
|
if (!File.Exists(revisionFile))
|
||||||
{
|
{
|
||||||
if (CheckRevision())
|
return false;
|
||||||
{
|
|
||||||
Console.WriteLine($"Данные Aonprd актуальны, текущая ревизия {Revision}.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Console.WriteLine($"Данные Aonprd устарели, обновляем {RepositoryDirPath} до ревизии {Revision}...");
|
|
||||||
|
|
||||||
if (Directory.Exists(RepositoryDirPath))
|
|
||||||
{
|
|
||||||
Directory.Delete(RepositoryDirPath, true);
|
|
||||||
}
|
|
||||||
Directory.CreateDirectory(RepositoryDirPath);
|
|
||||||
|
|
||||||
WriteFile("revision.txt", Revision.ToString());
|
|
||||||
|
|
||||||
Console.WriteLine("Загрузка индекса...");
|
|
||||||
var indexContent = Load($"/json-data/aon{Revision}-index.json");
|
|
||||||
WriteFile("index.json", indexContent);
|
|
||||||
|
|
||||||
var indexJson = JObject.Parse(indexContent);
|
|
||||||
|
|
||||||
Console.WriteLine("Загрузка индексных файлов...");
|
|
||||||
|
|
||||||
var bar = new ProgressBar(indexJson.Count);
|
|
||||||
var i = 0;
|
|
||||||
|
|
||||||
foreach (var kvp in indexJson)
|
|
||||||
{
|
|
||||||
var fileName = kvp.Key;
|
|
||||||
bar.Refresh(i++, fileName);
|
|
||||||
|
|
||||||
var fileContent = Load($"/json-data/{fileName}.json");
|
|
||||||
WriteFile($"{fileName}.json", fileContent);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bar.Refresh(indexJson.Count, "Готово!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
using StreamReader reader = new(revisionFile);
|
||||||
/// Проверка актуальности данных
|
string lastRevision = reader.ReadToEnd();
|
||||||
/// </summary>
|
return lastRevision == Revision.ToString();
|
||||||
/// <returns>true - актуальная ревизия, false - устаревшая/отсутсвует</returns>
|
}
|
||||||
private bool CheckRevision()
|
|
||||||
|
private void WriteFile(string path, string content)
|
||||||
|
{
|
||||||
|
string filePath = Path.Combine(RepositoryDirPath, path);
|
||||||
|
if (File.Exists(filePath))
|
||||||
{
|
{
|
||||||
var page = JObject.Parse(Load("/aon/_doc/feat-1"));
|
File.Delete(filePath);
|
||||||
|
|
||||||
Revision = Convert.ToInt32(page.SelectToken("_index")?.ToObject<string>()?.Replace("aon", "") ?? throw new Exception("Can't get revision"));
|
|
||||||
|
|
||||||
var revisionFile = Path.Combine(RepositoryDirPath, "revision.txt");
|
|
||||||
if (!File.Exists(revisionFile))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
using var reader = new StreamReader(revisionFile);
|
|
||||||
var lastRevision = reader.ReadToEnd();
|
|
||||||
return lastRevision == Revision.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void WriteFile (string path, string content)
|
|
||||||
{
|
|
||||||
var filePath = Path.Combine(RepositoryDirPath, path);
|
|
||||||
if (File.Exists(filePath))
|
|
||||||
{
|
|
||||||
File.Delete(filePath);
|
|
||||||
}
|
|
||||||
using var writer = new StreamWriter(filePath);
|
|
||||||
writer.Write(content);
|
|
||||||
}
|
}
|
||||||
|
using StreamWriter writer = new(filePath);
|
||||||
|
writer.Write(content);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,28 +1,27 @@
|
|||||||
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.Compendium
|
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.Compendium;
|
||||||
|
|
||||||
|
internal class AonprdCompendium
|
||||||
{
|
{
|
||||||
internal class AonprdCompendium
|
public readonly Dictionary<string, AonprdType> Types = [];
|
||||||
|
|
||||||
|
public void AddReference(string id, string file)
|
||||||
{
|
{
|
||||||
public readonly Dictionary<string, AonprdType> Types = [];
|
AonprdReference reference = new(id, file);
|
||||||
|
|
||||||
public void AddReference(string id, string file)
|
if (Types.TryGetValue(reference.Type, out AonprdType? type))
|
||||||
{
|
{
|
||||||
AonprdReference reference = new(id, file);
|
type.Add(reference);
|
||||||
|
}
|
||||||
if (Types.TryGetValue(reference.Type, out var type))
|
else
|
||||||
{
|
{
|
||||||
type.Add(reference);
|
Types.Add(reference.Type, new AonprdType(reference));
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Types.Add(reference.Type, new AonprdType(reference));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Count => Types.Values.Sum(x => x.Count);
|
|
||||||
|
|
||||||
public IEnumerable<string> Files(IEnumerable<string> types) => Types.Values
|
|
||||||
.Where(t => types.Contains(t.Type))
|
|
||||||
.SelectMany(t => t.Files)
|
|
||||||
.Distinct();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int Count => Types.Values.Sum(x => x.Count);
|
||||||
|
|
||||||
|
public IEnumerable<string> Files(IEnumerable<string> types) => Types.Values
|
||||||
|
.Where(t => types.Contains(t.Type))
|
||||||
|
.SelectMany(t => t.Files)
|
||||||
|
.Distinct();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,35 +1,23 @@
|
|||||||
|
|
||||||
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.Compendium
|
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.Compendium;
|
||||||
|
|
||||||
|
internal class AonprdReference(string id, string file)
|
||||||
{
|
{
|
||||||
internal class AonprdReference(string id, string file)
|
public readonly string Id = id;
|
||||||
|
|
||||||
|
public readonly string Type = ResolveType(id);
|
||||||
|
|
||||||
|
public readonly string File = file;
|
||||||
|
|
||||||
|
public static string ResolveType(string id)
|
||||||
{
|
{
|
||||||
public readonly string Id = id;
|
static string? subType(string type, string cat)
|
||||||
|
|
||||||
public readonly string Type = ResolveType(id);
|
|
||||||
|
|
||||||
public readonly string File = file;
|
|
||||||
|
|
||||||
public static string ResolveType(string id)
|
|
||||||
{
|
{
|
||||||
static string? subType(string type, string cat)
|
return type.StartsWith($"{cat}-") && type != cat ? type.EndsWith("-bonus") ? $"{cat}-bonus" : $"{cat}-sub" : null;
|
||||||
{
|
|
||||||
if (type.StartsWith($"{cat}-") && type != cat)
|
|
||||||
{
|
|
||||||
if (type.EndsWith("-bonus"))
|
|
||||||
{
|
|
||||||
return $"{cat}-bonus";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return $"{cat}-sub";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var type = string.Join('-', id.Split('-')[0..^1]);
|
|
||||||
|
|
||||||
return subType(type, "equipment") ?? subType(type, "weapon") ?? type;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string type = string.Join('-', id.Split('-')[0..^1]);
|
||||||
|
|
||||||
|
return subType(type, "equipment") ?? subType(type, "weapon") ?? type;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +1,22 @@
|
|||||||
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.Compendium
|
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.Compendium;
|
||||||
|
|
||||||
|
internal class AonprdType(string type)
|
||||||
{
|
{
|
||||||
internal class AonprdType(string type)
|
public readonly string Type = type;
|
||||||
|
|
||||||
|
public readonly List<AonprdReference> Items = [];
|
||||||
|
|
||||||
|
public AonprdType(AonprdReference reference) : this(reference.Type)
|
||||||
{
|
{
|
||||||
public readonly string Type = type;
|
Items.Add(reference);
|
||||||
|
|
||||||
public readonly List<AonprdReference> Items = [];
|
|
||||||
|
|
||||||
public AonprdType(AonprdReference reference) : this(reference.Type)
|
|
||||||
{
|
|
||||||
Items.Add(reference);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Add(AonprdReference reference)
|
|
||||||
{
|
|
||||||
Items.Add(reference);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<string> Files => [.. Items.Select(x => x.File).Distinct()];
|
|
||||||
|
|
||||||
public int Count => Items.Count;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Add(AonprdReference reference)
|
||||||
|
{
|
||||||
|
Items.Add(reference);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<string> Files => [.. Items.Select(x => x.File).Distinct()];
|
||||||
|
|
||||||
|
public int Count => Items.Count;
|
||||||
}
|
}
|
||||||
|
|||||||
+72
-73
@@ -2,86 +2,85 @@
|
|||||||
using Pf2eDataScrapper.Importers.Implementations.Aonprd.Compendium;
|
using Pf2eDataScrapper.Importers.Implementations.Aonprd.Compendium;
|
||||||
using Pf2eDataScrapper.SchemaBuilder;
|
using Pf2eDataScrapper.SchemaBuilder;
|
||||||
|
|
||||||
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.SchemaBuilder
|
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.SchemaBuilder;
|
||||||
|
|
||||||
|
internal class AonprdSchemaBuilder(AonprdLoader loader)
|
||||||
{
|
{
|
||||||
internal class AonprdSchemaBuilder(AonprdLoader loader)
|
public readonly AonprdLoader Loader = loader;
|
||||||
|
|
||||||
|
public readonly AonprdCompendium Compendium = loader.Compendium;
|
||||||
|
|
||||||
|
// public readonly AonprdSchemaTypeResolver Resolver = new();
|
||||||
|
|
||||||
|
public readonly Dictionary<string, SchemaNode> Schemas = [];
|
||||||
|
|
||||||
|
private SchemaNode GetSchema(AonprdLoader.LoaderEntry entry)
|
||||||
{
|
{
|
||||||
public readonly AonprdLoader Loader = loader;
|
string key = entry.Type;
|
||||||
|
if (!Schemas.TryGetValue(key, out SchemaNode? value))
|
||||||
public readonly AonprdCompendium Compendium = loader.Compendium;
|
|
||||||
|
|
||||||
// public readonly AonprdSchemaTypeResolver Resolver = new();
|
|
||||||
|
|
||||||
public readonly Dictionary<string, SchemaNode> Schemas = [];
|
|
||||||
|
|
||||||
private SchemaNode GetSchema(AonprdLoader.LoaderEntry entry)
|
|
||||||
{
|
{
|
||||||
string key = entry.Type;
|
value = new SchemaNode()
|
||||||
if (!Schemas.TryGetValue(key, out SchemaNode? value))
|
|
||||||
{
|
{
|
||||||
value = new SchemaNode()
|
// Resolver = Resolver,
|
||||||
{
|
Path = key
|
||||||
// Resolver = Resolver,
|
};
|
||||||
Path = key
|
Schemas[key] = value;
|
||||||
};
|
}
|
||||||
Schemas[key] = value;
|
return value;
|
||||||
}
|
}
|
||||||
return value;
|
|
||||||
|
public void BuildSchemas()
|
||||||
|
{
|
||||||
|
Console.WriteLine("Building schemas...");
|
||||||
|
|
||||||
|
ProgressBar bar = new ProgressBar(Loader.ItemCount);
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
foreach (AonprdLoader.LoaderEntry entry in Loader.Entries())
|
||||||
|
{
|
||||||
|
bar.Refresh(i++, entry.Type);
|
||||||
|
GetSchema(entry).AddValue(entry.Json);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void BuildSchemas()
|
bar.Refresh(Loader.ItemCount, "Done!");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Save(string targetDir)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Dumping schemas...");
|
||||||
|
|
||||||
|
targetDir = Path.Combine(targetDir, Loader.Importer.Slug);
|
||||||
|
|
||||||
|
ProgressBar bar = new(Schemas.Count);
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
foreach (KeyValuePair<string, SchemaNode> kvp in Schemas)
|
||||||
{
|
{
|
||||||
Console.WriteLine("Building schemas...");
|
bar.Refresh(i++, kvp.Key);
|
||||||
|
SaveSchema(targetDir, kvp.Key, kvp.Value);
|
||||||
var bar = new ProgressBar(Loader.ItemCount);
|
|
||||||
var i = 0;
|
|
||||||
|
|
||||||
foreach (var entry in Loader.Entries())
|
|
||||||
{
|
|
||||||
bar.Refresh(i++, entry.Type);
|
|
||||||
GetSchema(entry).AddValue(entry.Json);
|
|
||||||
}
|
|
||||||
|
|
||||||
bar.Refresh(Loader.ItemCount, "Done!");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Save(string targetDir)
|
|
||||||
{
|
|
||||||
Console.WriteLine("Dumping schemas...");
|
|
||||||
|
|
||||||
targetDir = Path.Combine(targetDir, Loader.Importer.Slug);
|
|
||||||
|
|
||||||
var bar = new ProgressBar(Schemas.Count);
|
|
||||||
var i = 0;
|
|
||||||
|
|
||||||
foreach (var kvp in Schemas)
|
|
||||||
{
|
|
||||||
bar.Refresh(i++, kvp.Key);
|
|
||||||
SaveSchema(targetDir, kvp.Key, kvp.Value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void SaveSchema(string dir, string name, SchemaNode schema)
|
|
||||||
{
|
|
||||||
var invalids = Path.GetInvalidFileNameChars();
|
|
||||||
var fileName = string.Join("_", schema.Path.Split(invalids, StringSplitOptions.RemoveEmptyEntries)).TrimEnd('.');
|
|
||||||
var path = Path.Combine(dir, fileName + ".yaml");
|
|
||||||
|
|
||||||
var dirPath = Path.GetDirectoryName(path);
|
|
||||||
if (dirPath is not null && !Directory.Exists(dirPath))
|
|
||||||
{
|
|
||||||
Directory.CreateDirectory(dirPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
using var writer = new StreamWriter(path);
|
|
||||||
|
|
||||||
schema.Beautify();
|
|
||||||
|
|
||||||
writer.WriteLine(SchemaNode.FormatYaml(new Dictionary<string, string>()
|
|
||||||
{
|
|
||||||
["name"] = name,
|
|
||||||
["values"] = schema.Values.ToString()
|
|
||||||
}, schema.Types[0].GetYamlData()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void SaveSchema(string dir, string name, SchemaNode schema)
|
||||||
|
{
|
||||||
|
char[] invalids = Path.GetInvalidFileNameChars();
|
||||||
|
string fileName = string.Join("_", schema.Path.Split(invalids, StringSplitOptions.RemoveEmptyEntries)).TrimEnd('.');
|
||||||
|
string path = Path.Combine(dir, fileName + ".yaml");
|
||||||
|
|
||||||
|
string? dirPath = Path.GetDirectoryName(path);
|
||||||
|
if (dirPath is not null && !Directory.Exists(dirPath))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(dirPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
using StreamWriter writer = new(path);
|
||||||
|
|
||||||
|
schema.Beautify();
|
||||||
|
|
||||||
|
writer.WriteLine(SchemaNode.FormatYaml(new Dictionary<string, string>()
|
||||||
|
{
|
||||||
|
["name"] = name,
|
||||||
|
["values"] = schema.Values.ToString()
|
||||||
|
}, schema.Types[0].GetYamlData()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+15
-16
@@ -1,18 +1,17 @@
|
|||||||
namespace Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry.Compendium
|
namespace Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry.Compendium;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Тип документов компендиума.
|
||||||
|
/// </summary>
|
||||||
|
internal enum CompendiumDocumentType
|
||||||
{
|
{
|
||||||
/// <summary>
|
Actor,
|
||||||
/// Тип документов компендиума.
|
Adventure,
|
||||||
/// </summary>
|
Cards,
|
||||||
internal enum CompendiumDocumentType
|
Item,
|
||||||
{
|
JournalEntry,
|
||||||
Actor,
|
Macro,
|
||||||
Adventure,
|
Playlist,
|
||||||
Cards,
|
RollTable,
|
||||||
Item,
|
Scene
|
||||||
JournalEntry,
|
|
||||||
Macro,
|
|
||||||
Playlist,
|
|
||||||
RollTable,
|
|
||||||
Scene
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+74
-75
@@ -1,88 +1,87 @@
|
|||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
namespace Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry.Compendium
|
namespace Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry.Compendium;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Папка компендиума.
|
||||||
|
/// </summary>
|
||||||
|
internal class CompendiumFolder
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Папка компендиума.
|
/// Идентификатор папки.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal class CompendiumFolder
|
public readonly string Id;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Название папки.
|
||||||
|
/// </summary>
|
||||||
|
public readonly string Name;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Путь к папке.
|
||||||
|
/// </summary>
|
||||||
|
public readonly string? Path;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Указатель, что папка содержится в других папках.
|
||||||
|
/// </summary>
|
||||||
|
public readonly bool IsContained;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Родительская папка.
|
||||||
|
/// </summary>
|
||||||
|
public CompendiumFolder? Parent;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Тип документов в папке.
|
||||||
|
/// </summary>
|
||||||
|
public readonly CompendiumDocumentType Type;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Конструктор папки компендиума из JSON-объекта.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="json"></param>
|
||||||
|
public CompendiumFolder(JObject json)
|
||||||
{
|
{
|
||||||
/// <summary>
|
if (json.TryGetValue("_id", out JToken? id))
|
||||||
/// Идентификатор папки.
|
|
||||||
/// </summary>
|
|
||||||
public readonly string Id;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Название папки.
|
|
||||||
/// </summary>
|
|
||||||
public readonly string Name;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Путь к папке.
|
|
||||||
/// </summary>
|
|
||||||
public readonly string? Path;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Указатель, что папка содержится в других папках.
|
|
||||||
/// </summary>
|
|
||||||
public readonly bool IsContained;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Родительская папка.
|
|
||||||
/// </summary>
|
|
||||||
public CompendiumFolder? Parent;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Тип документов в папке.
|
|
||||||
/// </summary>
|
|
||||||
public readonly CompendiumDocumentType Type;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Конструктор папки компендиума из JSON-объекта.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="json"></param>
|
|
||||||
public CompendiumFolder(JObject json)
|
|
||||||
{
|
{
|
||||||
if (json.TryGetValue("_id", out var id))
|
// Вложенная папка через _folders.json
|
||||||
{
|
Id = id!.ToString();
|
||||||
// Вложенная папка через _folders.json
|
Name = json["name"]!.ToString();
|
||||||
Id = id!.ToString();
|
|
||||||
Name = json["name"]!.ToString();
|
|
||||||
Path = null;
|
|
||||||
IsContained = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Корневой пак в system.json
|
|
||||||
Id = json["name"]!.ToString();
|
|
||||||
Name = json["label"]!.ToString();
|
|
||||||
Path = json["path"]!.ToString();
|
|
||||||
IsContained = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Type = (CompendiumDocumentType)Enum.Parse(typeof(CompendiumDocumentType), json["type"]!.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Конструктор папки компендиума.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="id">Идентификатор папки.</param>
|
|
||||||
/// <param name="name">Название папки.</param>
|
|
||||||
public CompendiumFolder(string id, string name)
|
|
||||||
{
|
|
||||||
Id = id;
|
|
||||||
Name = name;
|
|
||||||
Path = null;
|
Path = null;
|
||||||
|
IsContained = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Корневой пак в system.json
|
||||||
|
Id = json["name"]!.ToString();
|
||||||
|
Name = json["label"]!.ToString();
|
||||||
|
Path = json["path"]!.ToString();
|
||||||
IsContained = false;
|
IsContained = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
Type = (CompendiumDocumentType) Enum.Parse(typeof(CompendiumDocumentType), json["type"]!.ToString());
|
||||||
/// Строковое представление данных о папки компендиума.
|
}
|
||||||
/// </summary>
|
|
||||||
/// <returns>Строковое представление данных о папки компендиума.</returns>
|
/// <summary>
|
||||||
public override string ToString()
|
/// Конструктор папки компендиума.
|
||||||
{
|
/// </summary>
|
||||||
return $"CompendiumFolder([Id={Id}][Type={Type}][Name={Name}][Path={Path}][Parent={Parent?.Name ?? "null"}])";
|
/// <param name="id">Идентификатор папки.</param>
|
||||||
}
|
/// <param name="name">Название папки.</param>
|
||||||
|
public CompendiumFolder(string id, string name)
|
||||||
|
{
|
||||||
|
Id = id;
|
||||||
|
Name = name;
|
||||||
|
Path = null;
|
||||||
|
IsContained = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Строковое представление данных о папки компендиума.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Строковое представление данных о папки компендиума.</returns>
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return $"CompendiumFolder([Id={Id}][Type={Type}][Name={Name}][Path={Path}][Parent={Parent?.Name ?? "null"}])";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+20
-21
@@ -1,28 +1,27 @@
|
|||||||
namespace Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry.Compendium
|
namespace Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry.Compendium;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Коллекция папок компендиума
|
||||||
|
/// </summary>
|
||||||
|
internal class CompendiumFolderCollection : List<CompendiumFolder>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Коллекция папок компендиума
|
/// Получение папки по идентификатору
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal class CompendiumFolderCollection : List<CompendiumFolder>
|
/// <param name="id">Искомый идентификатор</param>
|
||||||
|
/// <returns>Искомая папка</returns>
|
||||||
|
public CompendiumFolder? GetById(string id)
|
||||||
{
|
{
|
||||||
/// <summary>
|
return this.FirstOrDefault(x => x.Id == id);
|
||||||
/// Получение папки по идентификатору
|
}
|
||||||
/// </summary>
|
|
||||||
/// <param name="id">Искомый идентификатор</param>
|
|
||||||
/// <returns>Искомая папка</returns>
|
|
||||||
public CompendiumFolder? GetById(string id)
|
|
||||||
{
|
|
||||||
return this.FirstOrDefault(x => x.Id == id);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Фильтрация требуемых папок компендиума.
|
/// Фильтрация требуемых папок компендиума.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public CompendiumFolderCollection Filter()
|
public CompendiumFolderCollection Filter()
|
||||||
{
|
{
|
||||||
// return [.. this.FindAll(x => x.Type != CompendiumDocumentType.Actor)];
|
// return [.. this.FindAll(x => x.Type != CompendiumDocumentType.Actor)];
|
||||||
return [.. this.FindAll(x => x.Type != CompendiumDocumentType.Actor && x.Name == "Conditions")];
|
return [.. this.FindAll(x => x.Type != CompendiumDocumentType.Actor && x.Name == "Conditions")];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,170 +1,169 @@
|
|||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry.Compendium;
|
using Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry.Compendium;
|
||||||
|
|
||||||
namespace Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry
|
namespace Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Загрузчик данных компендиума
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="importer">Импортёр</param>
|
||||||
|
internal class CompendiumLoader(Pf2EFoundryImporter importer)
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Загрузчик данных компендиума
|
/// Импортёр
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="importer">Импортёр</param>
|
public readonly Pf2EFoundryImporter Importer = importer;
|
||||||
internal class CompendiumLoader(Pf2EFoundryImporter importer)
|
|
||||||
|
/// <summary>
|
||||||
|
/// Набор папок компендиума
|
||||||
|
/// </summary>
|
||||||
|
public CompendiumFolderCollection Collection = [];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Счётчик папок
|
||||||
|
/// </summary>
|
||||||
|
private int _folderCount = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Общее количество элементов в компендиуме
|
||||||
|
/// </summary>
|
||||||
|
public int ItemCount { get; protected set; } = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Обработка папок компендиума в поле `packFolders`.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="folders"></param>
|
||||||
|
/// <param name="parent"></param>
|
||||||
|
protected void ProcessPackFolders(JArray folders, CompendiumFolder? parent)
|
||||||
{
|
{
|
||||||
/// <summary>
|
foreach (JToken token in folders)
|
||||||
/// Импортёр
|
|
||||||
/// </summary>
|
|
||||||
public readonly Pf2EFoundryImporter Importer = importer;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Набор папок компендиума
|
|
||||||
/// </summary>
|
|
||||||
public CompendiumFolderCollection Collection = [];
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Счётчик папок
|
|
||||||
/// </summary>
|
|
||||||
private int _folderCount = 0;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Общее количество элементов в компендиуме
|
|
||||||
/// </summary>
|
|
||||||
public int ItemCount { get; protected set; } = 0;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Обработка папок компендиума в поле `packFolders`.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="folders"></param>
|
|
||||||
/// <param name="parent"></param>
|
|
||||||
protected void ProcessPackFolders(JArray folders, CompendiumFolder? parent)
|
|
||||||
{
|
{
|
||||||
foreach (var token in folders)
|
JObject obj = (JObject) token;
|
||||||
|
|
||||||
|
string id = $"folder-{_folderCount++}";
|
||||||
|
string name = obj["name"]!.ToString();
|
||||||
|
|
||||||
|
CompendiumFolder folder = new(id, name)
|
||||||
{
|
{
|
||||||
JObject obj = (JObject)token;
|
Parent = parent
|
||||||
|
};
|
||||||
|
Collection.Add(folder);
|
||||||
|
|
||||||
var id = $"folder-{_folderCount++}";
|
if (obj.TryGetValue("packs", out JToken? packs))
|
||||||
var name = obj["name"]!.ToString();
|
{
|
||||||
|
foreach (JToken jToken in packs!)
|
||||||
var folder = new CompendiumFolder(id, name)
|
|
||||||
{
|
{
|
||||||
Parent = parent
|
string packId = jToken.ToString();
|
||||||
};
|
CompendiumFolder? child = Collection.GetById(packId);
|
||||||
Collection.Add(folder);
|
|
||||||
|
|
||||||
if (obj.TryGetValue("packs", out var packs))
|
if (child is not null)
|
||||||
{
|
|
||||||
foreach (var jToken in packs!)
|
|
||||||
{
|
{
|
||||||
var packId = jToken.ToString();
|
child.Parent = folder;
|
||||||
var child = Collection.GetById(packId);
|
|
||||||
|
|
||||||
if (child is not null)
|
|
||||||
{
|
|
||||||
child.Parent = folder;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (obj.TryGetValue("folders", out var innerFolders))
|
|
||||||
{
|
|
||||||
ProcessPackFolders((JArray)innerFolders!, folder);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Collection = Collection.Filter();
|
if (obj.TryGetValue("folders", out JToken? innerFolders))
|
||||||
|
{
|
||||||
|
ProcessPackFolders((JArray) innerFolders!, folder);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
Collection = Collection.Filter();
|
||||||
/// Подготовка набора папок компендиума
|
}
|
||||||
/// </summary>
|
|
||||||
public void Prepare()
|
/// <summary>
|
||||||
|
/// Подготовка набора папок компендиума
|
||||||
|
/// </summary>
|
||||||
|
public void Prepare()
|
||||||
|
{
|
||||||
|
using StreamReader systemFile = new(Path.Combine(Importer.Dir, "static", "system.json"));
|
||||||
|
JObject systemData = JObject.Parse(systemFile.ReadToEnd());
|
||||||
|
|
||||||
|
foreach (JToken pack in systemData["packs"]!)
|
||||||
{
|
{
|
||||||
using var systemFile = new StreamReader(Path.Combine(Importer.Dir, "static", "system.json"));
|
CompendiumFolder folder = new((JObject) pack);
|
||||||
var systemData = JObject.Parse(systemFile.ReadToEnd());
|
|
||||||
|
|
||||||
foreach (var pack in systemData["packs"]!)
|
Collection.Add(folder);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (systemData.TryGetValue("packFolders", out JToken? packFolders))
|
||||||
|
{
|
||||||
|
ProcessPackFolders((JArray) packFolders, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Поиск _folders.json
|
||||||
|
foreach (CompendiumFolder? parent in Collection.ToList())
|
||||||
|
{
|
||||||
|
if (parent.Path is null)
|
||||||
{
|
{
|
||||||
var folder = new CompendiumFolder((JObject)pack);
|
continue;
|
||||||
|
|
||||||
Collection.Add(folder);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (systemData.TryGetValue("packFolders", out var packFolders))
|
string dirPath = Path.Combine(Importer.Dir, parent.Path);
|
||||||
{
|
|
||||||
ProcessPackFolders((JArray)packFolders, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Поиск _folders.json
|
ItemCount += Directory.GetFiles(dirPath, "*.json", SearchOption.AllDirectories)
|
||||||
foreach (var parent in Collection.ToList())
|
.Length;
|
||||||
|
|
||||||
|
foreach (string foldersFilePath in Directory.GetFiles(dirPath, "_folders.json", SearchOption.AllDirectories))
|
||||||
{
|
{
|
||||||
if (parent.Path is null)
|
ItemCount--;
|
||||||
|
using StreamReader file = new(foldersFilePath);
|
||||||
|
JArray data = JArray.Parse(file.ReadToEnd());
|
||||||
|
|
||||||
|
foreach (JToken pack in data)
|
||||||
|
{
|
||||||
|
CompendiumFolder folder = new((JObject) pack) { Parent = parent };
|
||||||
|
Collection.Add(folder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// foreach (var folder in Collection)
|
||||||
|
// {
|
||||||
|
// Console.WriteLine(folder);
|
||||||
|
// }
|
||||||
|
|
||||||
|
Collection = Collection.Filter();
|
||||||
|
|
||||||
|
Console.WriteLine($"Total items: {ItemCount}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<LoaderEnrty> Enrties()
|
||||||
|
{
|
||||||
|
foreach (CompendiumFolder folder in Collection.FindAll(f => f.Path is not null))
|
||||||
|
{
|
||||||
|
string dirPath = Path.Combine(Importer.Dir, folder.Path!);
|
||||||
|
|
||||||
|
foreach (string filePath in Directory.GetFiles(dirPath, "*.json", SearchOption.AllDirectories))
|
||||||
|
{
|
||||||
|
FileInfo file = new(filePath);
|
||||||
|
|
||||||
|
if (file.Name == "_folders.json")
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var dirPath = Path.Combine(Importer.Dir, parent.Path);
|
using StreamReader reader = new(file.FullName);
|
||||||
|
JObject itemJson = JObject.Parse(reader.ReadToEnd());
|
||||||
|
|
||||||
ItemCount += Directory.GetFiles(dirPath, "*.json", SearchOption.AllDirectories)
|
CompendiumFolder? itemFolder = folder;
|
||||||
.Length;
|
if (itemJson.TryGetValue("folder", out JToken? itemJsonFolder))
|
||||||
|
|
||||||
foreach (var foldersFilePath in Directory.GetFiles(dirPath, "_folders.json", SearchOption.AllDirectories))
|
|
||||||
{
|
{
|
||||||
ItemCount--;
|
itemFolder = Collection.GetById(itemJsonFolder.ToString());
|
||||||
using var file = new StreamReader(foldersFilePath);
|
|
||||||
var data = JArray.Parse(file.ReadToEnd());
|
|
||||||
|
|
||||||
foreach (var pack in data)
|
|
||||||
{
|
|
||||||
var folder = new CompendiumFolder((JObject)pack) { Parent = parent };
|
|
||||||
Collection.Add(folder);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
yield return new LoaderEnrty(file, itemJson, itemFolder ?? folder);
|
||||||
}
|
}
|
||||||
|
|
||||||
// foreach (var folder in Collection)
|
|
||||||
// {
|
|
||||||
// Console.WriteLine(folder);
|
|
||||||
// }
|
|
||||||
|
|
||||||
Collection = Collection.Filter();
|
|
||||||
|
|
||||||
Console.WriteLine($"Total items: {ItemCount}");
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<LoaderEnrty> Enrties()
|
|
||||||
{
|
|
||||||
foreach (var folder in Collection.FindAll(f => f.Path is not null))
|
|
||||||
{
|
|
||||||
var dirPath = Path.Combine(Importer.Dir, folder.Path!);
|
|
||||||
|
|
||||||
foreach (var filePath in Directory.GetFiles(dirPath, "*.json", SearchOption.AllDirectories))
|
|
||||||
{
|
|
||||||
var file = new FileInfo(filePath);
|
|
||||||
|
|
||||||
if (file.Name == "_folders.json")
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
using var reader = new StreamReader(file.FullName);
|
|
||||||
var itemJson = JObject.Parse(reader.ReadToEnd());
|
|
||||||
|
|
||||||
var itemFolder = folder;
|
|
||||||
if (itemJson.TryGetValue("folder", out var itemJsonFolder))
|
|
||||||
{
|
|
||||||
itemFolder = Collection.GetById(itemJsonFolder.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
yield return new LoaderEnrty(file, itemJson, itemFolder ?? folder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class LoaderEnrty(FileInfo file, JObject itemJson, CompendiumFolder folder)
|
|
||||||
{
|
|
||||||
public FileInfo File { get; } = file;
|
|
||||||
public JObject Json { get; } = itemJson;
|
|
||||||
public CompendiumFolder Folder { get; } = folder;
|
|
||||||
public string Type { get; } = itemJson["type"]?.ToString()
|
|
||||||
?? throw new Exception($"Expected to have `type` field in file {file.FullName}");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class LoaderEnrty(FileInfo file, JObject itemJson, CompendiumFolder folder)
|
||||||
|
{
|
||||||
|
public FileInfo File { get; } = file;
|
||||||
|
public JObject Json { get; } = itemJson;
|
||||||
|
public CompendiumFolder Folder { get; } = folder;
|
||||||
|
public string Type { get; } = itemJson["type"]?.ToString()
|
||||||
|
?? throw new Exception($"Expected to have `type` field in file {file.FullName}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+79
-86
@@ -1,104 +1,97 @@
|
|||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using NpgsqlTypes;
|
using NpgsqlTypes;
|
||||||
using Pf2eDataScrapper.Model;
|
using Pf2eDataScrapper.Model;
|
||||||
|
using Pf2eDataScrapper.Model.Common;
|
||||||
using Pf2eDataScrapper.Model.Enum;
|
using Pf2eDataScrapper.Model.Enum;
|
||||||
using System.ComponentModel;
|
|
||||||
|
|
||||||
namespace Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry.DataBuilder
|
namespace Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry.DataBuilder;
|
||||||
|
|
||||||
|
internal abstract class AbstractProcessor(JObject input)
|
||||||
{
|
{
|
||||||
internal abstract class AbstractProcessor(JObject input)
|
public readonly Pf2eTestContext Context = new();
|
||||||
|
|
||||||
|
public readonly JObject Input = input;
|
||||||
|
|
||||||
|
protected DataBuilderHelper Helper = DataBuilderHelper.Instance;
|
||||||
|
|
||||||
|
public Entity Entity { get; set; } = new();
|
||||||
|
|
||||||
|
public abstract EntityType Type { get; }
|
||||||
|
|
||||||
|
public abstract IEntity Object { get; }
|
||||||
|
|
||||||
|
public virtual void Process()
|
||||||
{
|
{
|
||||||
public readonly Pf2eTestContext Context = new();
|
Entity.Id = Helper.NextId;
|
||||||
|
Entity.Type = Type;
|
||||||
|
|
||||||
public readonly JObject Input = input;
|
Entity.Name = GetRequired<string>("name");
|
||||||
|
Entity.Img = GetRequired<string>("img");
|
||||||
|
Entity.Description = GetRequired<string>("system.description.value");
|
||||||
|
|
||||||
protected DataBuilderHelper Helper = DataBuilderHelper.Instance;
|
Entity.IsTranslated = false;
|
||||||
|
Entity.IsChecked = false;
|
||||||
|
|
||||||
public Entity Entity { get; set; } = new Entity();
|
Entity.Slug = Helper.GenerateSlug(Entity.Name);
|
||||||
|
|
||||||
public abstract EntityType Type { get; }
|
Object.Id = Entity.Id;
|
||||||
|
|
||||||
public abstract IEntity Object { get; }
|
Context.Entities.Add(Entity);
|
||||||
|
|
||||||
public virtual void Process()
|
// TODO Traits, Alias
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Save()
|
||||||
|
{
|
||||||
|
Context.SaveChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
public T GetRequired<T>(string path)
|
||||||
|
{
|
||||||
|
JToken token = Input.SelectToken(path) ?? throw new NullReferenceException($"'{path}' is null");
|
||||||
|
|
||||||
|
return token.ToObject<T>() ?? throw new NullReferenceException($"'{path}' is null");
|
||||||
|
}
|
||||||
|
|
||||||
|
public T? GetOptional<T>(string path)
|
||||||
|
{
|
||||||
|
JToken? token = Input.SelectToken(path) ?? null;
|
||||||
|
|
||||||
|
return token is null ? default! : token.ToObject<T>() ?? default;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T EnumRequired<T>(string path) where T : Enum
|
||||||
|
{
|
||||||
|
return EnumOptional<T>(path) ?? throw new Exception($"Can't resolve Enum value for {typeof(T).Name} in path {path}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public T? EnumOptional<T>(string path) where T : Enum
|
||||||
|
{
|
||||||
|
string? value = GetOptional<string>(path);
|
||||||
|
|
||||||
|
if (value == null)
|
||||||
{
|
{
|
||||||
Entity.Id = Helper.NextId;
|
|
||||||
Entity.Type = Type;
|
|
||||||
|
|
||||||
Entity.Name = GetRequired<string>("name");
|
|
||||||
Entity.Img = GetRequired<string>("img");
|
|
||||||
Entity.Description = GetRequired<string>("system.description.value");
|
|
||||||
Entity.DescriptionGm = GetOptional<string>("system.description.gm");
|
|
||||||
|
|
||||||
Entity.IsTranslated = false;
|
|
||||||
Entity.IsChecked = false;
|
|
||||||
|
|
||||||
Entity.Slug = Helper.GenerateSlug(Entity.Name);
|
|
||||||
|
|
||||||
Object.Id = Entity.Id;
|
|
||||||
|
|
||||||
Context.Entities.Add(Entity);
|
|
||||||
|
|
||||||
// TODO Traits, Alias
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Save()
|
|
||||||
{
|
|
||||||
Context.SaveChanges();
|
|
||||||
}
|
|
||||||
|
|
||||||
public T GetRequired<T>(string path)
|
|
||||||
{
|
|
||||||
var token = Input.SelectToken(path) ?? throw new NullReferenceException($"'{path}' is null");
|
|
||||||
|
|
||||||
return token.ToObject<T>() ?? throw new NullReferenceException($"'{path}' is null");
|
|
||||||
}
|
|
||||||
|
|
||||||
public T? GetOptional<T>(string path)
|
|
||||||
{
|
|
||||||
var token = Input.SelectToken(path) ?? null;
|
|
||||||
|
|
||||||
if (token is null)
|
|
||||||
{
|
|
||||||
return default!;
|
|
||||||
}
|
|
||||||
|
|
||||||
return token.ToObject<T>() ?? default;
|
|
||||||
}
|
|
||||||
|
|
||||||
public T EnumRequired<T>(string path) where T : Enum
|
|
||||||
{
|
|
||||||
return EnumOptional<T>(path) ?? throw new Exception($"Can't resolve Enum value for {typeof(T).Name} in path {path}");
|
|
||||||
}
|
|
||||||
|
|
||||||
public T? EnumOptional<T>(string path) where T : Enum
|
|
||||||
{
|
|
||||||
string? value = GetOptional<string>(path);
|
|
||||||
|
|
||||||
if (value == null)
|
|
||||||
{
|
|
||||||
return default;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var field in typeof(T).GetFields())
|
|
||||||
{
|
|
||||||
if (Attribute.GetCustomAttribute(field, typeof(PgNameAttribute)) is PgNameAttribute attribute)
|
|
||||||
{
|
|
||||||
if (attribute.PgName == value)
|
|
||||||
{
|
|
||||||
return (T)field.GetValue(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (field.Name == value)
|
|
||||||
{
|
|
||||||
return (T)field.GetValue(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return default;
|
return default;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach (System.Reflection.FieldInfo field in typeof(T).GetFields())
|
||||||
|
{
|
||||||
|
if (Attribute.GetCustomAttribute(field, typeof(PgNameAttribute)) is PgNameAttribute attribute)
|
||||||
|
{
|
||||||
|
if (attribute.PgName == value)
|
||||||
|
{
|
||||||
|
return (T) field.GetValue(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (field.Name == value)
|
||||||
|
{
|
||||||
|
return (T) field.GetValue(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return default;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+21
-22
@@ -1,28 +1,27 @@
|
|||||||
using Slugify;
|
using Slugify;
|
||||||
|
|
||||||
namespace Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry.DataBuilder
|
namespace Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry.DataBuilder;
|
||||||
|
|
||||||
|
internal class DataBuilderHelper
|
||||||
{
|
{
|
||||||
internal class DataBuilderHelper
|
public Pf2eDatabase Database = Pf2eDatabase.Instance;
|
||||||
|
|
||||||
|
public SlugHelper Slugger = new();
|
||||||
|
|
||||||
|
private DataBuilderHelper()
|
||||||
{
|
{
|
||||||
public Pf2eDatabase Database = Pf2eDatabase.Instance;
|
|
||||||
|
|
||||||
public SlugHelper Slugger = new();
|
|
||||||
|
|
||||||
private DataBuilderHelper()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
private int _id = 0;
|
|
||||||
|
|
||||||
public int NextId { get => ++_id; }
|
|
||||||
|
|
||||||
public string GenerateSlug(string name)
|
|
||||||
{
|
|
||||||
return Slugger.GenerateSlug(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public readonly static DataBuilderHelper Instance = new();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int _id = 0;
|
||||||
|
|
||||||
|
public int NextId { get => ++_id; }
|
||||||
|
|
||||||
|
public string GenerateSlug(string name)
|
||||||
|
{
|
||||||
|
return Slugger.GenerateSlug(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static readonly DataBuilderHelper Instance = new();
|
||||||
}
|
}
|
||||||
|
|||||||
+66
-72
@@ -3,84 +3,78 @@ using Newtonsoft.Json.Linq;
|
|||||||
using Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry.Compendium;
|
using Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry.Compendium;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry.DataBuilder
|
namespace Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry.DataBuilder;
|
||||||
|
|
||||||
|
internal class PF2eFoundryDataBuilder
|
||||||
{
|
{
|
||||||
internal class PF2eFoundryDataBuilder
|
public Pf2eDatabase Database = Pf2eDatabase.Instance;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Загрузчик данных.
|
||||||
|
/// </summary>
|
||||||
|
public readonly CompendiumLoader Loader;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Коллекция папок компендиума.
|
||||||
|
/// </summary>
|
||||||
|
public CompendiumFolderCollection Collection => Loader.Collection;
|
||||||
|
|
||||||
|
public Dictionary<string, ConstructorInfo> Processors = [];
|
||||||
|
|
||||||
|
public PF2eFoundryDataBuilder(CompendiumLoader loader)
|
||||||
{
|
{
|
||||||
public Pf2eDatabase Database = Pf2eDatabase.Instance;
|
Loader = loader;
|
||||||
|
PrepareProcessors();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
public void PrepareProcessors()
|
||||||
/// Загрузчик данных.
|
{
|
||||||
/// </summary>
|
foreach (Type type in Assembly.GetExecutingAssembly().GetTypes())
|
||||||
public readonly CompendiumLoader Loader;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Коллекция папок компендиума.
|
|
||||||
/// </summary>
|
|
||||||
public CompendiumFolderCollection Collection => Loader.Collection;
|
|
||||||
|
|
||||||
public Dictionary<string, ConstructorInfo> Processors = [];
|
|
||||||
|
|
||||||
public PF2eFoundryDataBuilder(CompendiumLoader loader)
|
|
||||||
{
|
{
|
||||||
Loader = loader;
|
if (type.GetCustomAttributes(typeof(Pf2eTypeProcessor), true).Length <= 0)
|
||||||
PrepareProcessors();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void PrepareProcessors()
|
|
||||||
{
|
|
||||||
foreach (var type in Assembly.GetExecutingAssembly().GetTypes())
|
|
||||||
{
|
{
|
||||||
if (type.GetCustomAttributes(typeof(Pf2eTypeProcessor), true).Length <= 0)
|
continue;
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
var attribute = type.GetCustomAttribute<Pf2eTypeProcessor>();
|
|
||||||
Processors.Add(attribute!.Type, type.GetConstructor(new[] { typeof(JObject) })!);
|
|
||||||
}
|
}
|
||||||
}
|
Pf2eTypeProcessor? attribute = type.GetCustomAttribute<Pf2eTypeProcessor>();
|
||||||
|
Processors.Add(attribute!.Type, type.GetConstructor([typeof(JObject)])!);
|
||||||
public AbstractProcessor? GetProcessor(string type, JObject input)
|
|
||||||
{
|
|
||||||
if (Processors.TryGetValue(type, out var constructor))
|
|
||||||
{
|
|
||||||
return (AbstractProcessor)constructor.Invoke([input]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void TruncateTables()
|
|
||||||
{
|
|
||||||
Console.WriteLine("Truncating all old data...");
|
|
||||||
|
|
||||||
Database.ExecuteCommand("SELECT f_truncate_tables();");
|
|
||||||
|
|
||||||
Console.WriteLine("Truncation completed!");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Build()
|
|
||||||
{
|
|
||||||
Console.WriteLine("Building data...");
|
|
||||||
|
|
||||||
var bar = new ProgressBar(Loader.ItemCount);
|
|
||||||
var i = 0;
|
|
||||||
|
|
||||||
foreach (var entry in Loader.Enrties())
|
|
||||||
{
|
|
||||||
bar.Refresh(i++, $"{entry.Folder.Name}/{entry.File.Name}");
|
|
||||||
|
|
||||||
var processor = GetProcessor($"{entry.Folder.Type}.{entry.Type}", entry.Json);
|
|
||||||
if (processor is null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
processor.Process();
|
|
||||||
processor.Save();
|
|
||||||
}
|
|
||||||
|
|
||||||
bar.Refresh(Loader.ItemCount, "Done!");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AbstractProcessor? GetProcessor(string type, JObject input)
|
||||||
|
{
|
||||||
|
return Processors.TryGetValue(type, out ConstructorInfo? constructor) ? (AbstractProcessor) constructor.Invoke([input]) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void TruncateTables()
|
||||||
|
{
|
||||||
|
Console.WriteLine("Truncating all old data...");
|
||||||
|
|
||||||
|
Database.ExecuteCommand("SELECT f_truncate_tables();");
|
||||||
|
|
||||||
|
Console.WriteLine("Truncation completed!");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Build()
|
||||||
|
{
|
||||||
|
Console.WriteLine("Building data...");
|
||||||
|
|
||||||
|
ProgressBar bar = new(Loader.ItemCount);
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
foreach (CompendiumLoader.LoaderEnrty entry in Loader.Enrties())
|
||||||
|
{
|
||||||
|
bar.Refresh(i++, $"{entry.Folder.Name}/{entry.File.Name}");
|
||||||
|
|
||||||
|
AbstractProcessor? processor = GetProcessor($"{entry.Folder.Type}.{entry.Type}", entry.Json);
|
||||||
|
if (processor is null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
processor.Process();
|
||||||
|
processor.Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
bar.Refresh(Loader.ItemCount, "Done!");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-8
@@ -1,10 +1,9 @@
|
|||||||
namespace Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry.DataBuilder
|
namespace Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry.DataBuilder;
|
||||||
{
|
|
||||||
[AttributeUsage(AttributeTargets.Class)]
|
|
||||||
internal class Pf2eTypeProcessor(string type, int priority = 0) : Attribute
|
|
||||||
{
|
|
||||||
public readonly string Type = type;
|
|
||||||
|
|
||||||
public readonly int Priority = priority;
|
[AttributeUsage(AttributeTargets.Class)]
|
||||||
}
|
internal class Pf2eTypeProcessor(string type, int priority = 0) : Attribute
|
||||||
|
{
|
||||||
|
public readonly string Type = type;
|
||||||
|
|
||||||
|
public readonly int Priority = priority;
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-20
@@ -2,32 +2,31 @@
|
|||||||
using Pf2eDataScrapper.Model;
|
using Pf2eDataScrapper.Model;
|
||||||
using Pf2eDataScrapper.Model.Enum;
|
using Pf2eDataScrapper.Model.Enum;
|
||||||
|
|
||||||
namespace Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry.DataBuilder.Processors
|
namespace Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry.DataBuilder.Processors;
|
||||||
|
|
||||||
|
[Pf2eTypeProcessor("Item.condition")]
|
||||||
|
internal class ConditionProcessor(JObject input) : AbstractProcessor(input)
|
||||||
{
|
{
|
||||||
[Pf2eTypeProcessor("Item.condition")]
|
public override EntityType Type => EntityType.Condition;
|
||||||
internal class ConditionProcessor(JObject input) : AbstractProcessor(input)
|
|
||||||
|
public override Condition Object { get; } = new();
|
||||||
|
|
||||||
|
public override void Process()
|
||||||
{
|
{
|
||||||
public override EntityType Type => EntityType.Condition;
|
base.Process();
|
||||||
|
|
||||||
public override Condition Object { get; } = new();
|
JObject extra = new()
|
||||||
|
|
||||||
public override void Process()
|
|
||||||
{
|
{
|
||||||
base.Process();
|
{ "duration", Input.SelectToken("system.duration") },
|
||||||
|
{ "overrides", Input.SelectToken("system.overrides") },
|
||||||
|
{ "references", Input.SelectToken("system.references") },
|
||||||
|
{ "value", Input.SelectToken("system.value") }
|
||||||
|
};
|
||||||
|
|
||||||
JObject extra = new()
|
Object.Extra = extra.ToString(Newtonsoft.Json.Formatting.Indented);
|
||||||
{
|
|
||||||
{ "duration", Input.SelectToken("system.duration") },
|
|
||||||
{ "overrides", Input.SelectToken("system.overrides") },
|
|
||||||
{ "references", Input.SelectToken("system.references") },
|
|
||||||
{ "value", Input.SelectToken("system.value") }
|
|
||||||
};
|
|
||||||
|
|
||||||
Object.Extra = extra.ToString(Newtonsoft.Json.Formatting.Indented);
|
Object.Group = EnumOptional<ConditionGroup>("system.group");
|
||||||
|
|
||||||
Object.Group = EnumOptional<ConditionGroup>("system.group");
|
Context.Conditions.Add(Object);
|
||||||
|
|
||||||
Context.Conditions.Add(Object);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,66 +2,65 @@
|
|||||||
using Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry.SchemaBuilder;
|
using Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry.SchemaBuilder;
|
||||||
using Pf2eDataScrapper.Importers.Repositories;
|
using Pf2eDataScrapper.Importers.Repositories;
|
||||||
|
|
||||||
namespace Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry
|
namespace Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Импортёр для модуля Pf2e FoundryVTT.
|
||||||
|
/// </summary>
|
||||||
|
internal class Pf2EFoundryImporter : AbstractImporter
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Импортёр для модуля Pf2e FoundryVTT.
|
/// Отображаемое имя импортёра.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal class Pf2EFoundryImporter : AbstractImporter
|
public override string Name => "Pf2e FoundryVTT module";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Короткое имя импортёра.
|
||||||
|
/// </summary>
|
||||||
|
public override string Slug => "pf2e";
|
||||||
|
|
||||||
|
public override AbstractImporterRepository Repository => new GitRepository(this, "https://github.com/foundryvtt/pf2e.git");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Загрузчик данных компендиума.
|
||||||
|
/// </summary>
|
||||||
|
public CompendiumLoader Loader { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Конструктор импортёра.
|
||||||
|
/// </summary>
|
||||||
|
public Pf2EFoundryImporter()
|
||||||
{
|
{
|
||||||
/// <summary>
|
Loader = new CompendiumLoader(this);
|
||||||
/// Отображаемое имя импортёра.
|
}
|
||||||
/// </summary>
|
|
||||||
public override string Name => "Pf2e FoundryVTT module";
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Короткое имя импортёра.
|
/// Метод подготовки данных для импорта.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public override string Slug => "pf2e";
|
public override void Prepare()
|
||||||
|
{
|
||||||
|
Loader.Prepare();
|
||||||
|
}
|
||||||
|
|
||||||
public override AbstractImporterRepository Repository => new GitRepository(this, "https://github.com/foundryvtt/pf2e.git");
|
/// <summary>
|
||||||
|
/// Импорт данных.
|
||||||
|
/// </summary>
|
||||||
|
public override void Import()
|
||||||
|
{
|
||||||
|
PF2eFoundryDataBuilder builder = new(Loader);
|
||||||
|
|
||||||
/// <summary>
|
builder.TruncateTables();
|
||||||
/// Загрузчик данных компендиума.
|
builder.Build();
|
||||||
/// </summary>
|
}
|
||||||
public CompendiumLoader Loader { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Конструктор импортёра.
|
/// Построение схем данных
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Pf2EFoundryImporter()
|
public override void BuildSchemas()
|
||||||
{
|
{
|
||||||
Loader = new CompendiumLoader(this);
|
CompendiumSchemaBuilder builder = new(Loader);
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
builder.BuildSchemas();
|
||||||
/// Метод подготовки данных для импорта.
|
builder.Save(EnvSettings.OutputDir);
|
||||||
/// </summary>
|
|
||||||
public override void Prepare()
|
|
||||||
{
|
|
||||||
Loader.Prepare();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Импорт данных.
|
|
||||||
/// </summary>
|
|
||||||
public override void Import()
|
|
||||||
{
|
|
||||||
var builder = new PF2eFoundryDataBuilder(Loader);
|
|
||||||
|
|
||||||
builder.TruncateTables();
|
|
||||||
builder.Build();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Построение схем данных
|
|
||||||
/// </summary>
|
|
||||||
public override void BuildSchemas()
|
|
||||||
{
|
|
||||||
var builder = new CompendiumSchemaBuilder(Loader);
|
|
||||||
|
|
||||||
builder.BuildSchemas();
|
|
||||||
builder.Save(EnvSettings.OutputDir);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+154
-160
@@ -2,191 +2,185 @@
|
|||||||
using Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry.Compendium;
|
using Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry.Compendium;
|
||||||
using Pf2eDataScrapper.SchemaBuilder;
|
using Pf2eDataScrapper.SchemaBuilder;
|
||||||
|
|
||||||
namespace Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry.SchemaBuilder
|
namespace Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry.SchemaBuilder;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Класс построения схемы компендиумов.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="loader">Загрузчик данных.</param>
|
||||||
|
internal class CompendiumSchemaBuilder(CompendiumLoader loader)
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Класс построения схемы компендиумов.
|
/// Загрузчик данных.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="loader">Загрузчик данных.</param>
|
public CompendiumLoader Loader = loader;
|
||||||
internal class CompendiumSchemaBuilder(CompendiumLoader loader)
|
|
||||||
|
/// <summary>
|
||||||
|
/// Коллекция папок компендиума.
|
||||||
|
/// </summary>
|
||||||
|
public CompendiumFolderCollection Collection = loader.Collection;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Объект для определения типов полей в компендиумах PF2E.
|
||||||
|
/// </summary>
|
||||||
|
public CompendiumSchemaTypeResolver Resolver = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Схемы данных по папкам.
|
||||||
|
/// </summary>
|
||||||
|
public Dictionary<CompendiumFolder, SchemaNode> PackSchemas = [];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Схемы данных по типам.
|
||||||
|
/// </summary>
|
||||||
|
public Dictionary<string, SchemaNode> TypeSchemas = [];
|
||||||
|
|
||||||
|
private readonly string[] _equipmentTypes = ["equipment", "armor", "backpack", "consumable", "kit", "shield", "treasure", "weapon"];
|
||||||
|
|
||||||
|
private SchemaNode GetPackSchema(CompendiumFolder folder)
|
||||||
{
|
{
|
||||||
/// <summary>
|
if (!PackSchemas.TryGetValue(folder, out SchemaNode? value))
|
||||||
/// Загрузчик данных.
|
|
||||||
/// </summary>
|
|
||||||
public CompendiumLoader Loader = loader;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Коллекция папок компендиума.
|
|
||||||
/// </summary>
|
|
||||||
public CompendiumFolderCollection Collection = loader.Collection;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Объект для определения типов полей в компендиумах PF2E.
|
|
||||||
/// </summary>
|
|
||||||
public CompendiumSchemaTypeResolver Resolver = new();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Схемы данных по папкам.
|
|
||||||
/// </summary>
|
|
||||||
public Dictionary<CompendiumFolder, SchemaNode> PackSchemas = [];
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Схемы данных по типам.
|
|
||||||
/// </summary>
|
|
||||||
public Dictionary<string, SchemaNode> TypeSchemas = [];
|
|
||||||
|
|
||||||
private readonly string[] _equipmentTypes = ["equipment", "armor", "backpack", "consumable", "kit", "shield", "treasure", "weapon"];
|
|
||||||
|
|
||||||
private SchemaNode GetPackSchema(CompendiumFolder folder)
|
|
||||||
{
|
{
|
||||||
if (!PackSchemas.TryGetValue(folder, out SchemaNode? value))
|
value = new SchemaNode()
|
||||||
{
|
{
|
||||||
value = new SchemaNode()
|
Resolver = Resolver,
|
||||||
{
|
Path = folder.Name
|
||||||
Resolver = Resolver,
|
};
|
||||||
Path = folder.Name
|
PackSchemas[folder] = value;
|
||||||
};
|
|
||||||
PackSchemas[folder] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private SchemaNode GetTypeSchema(string key)
|
return value;
|
||||||
{
|
}
|
||||||
if (!TypeSchemas.TryGetValue(key, out SchemaNode? value))
|
|
||||||
{
|
|
||||||
value = new SchemaNode()
|
|
||||||
{
|
|
||||||
Resolver = Resolver,
|
|
||||||
Path = key
|
|
||||||
};
|
|
||||||
TypeSchemas[key] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
private SchemaNode GetTypeSchema(string key)
|
||||||
|
{
|
||||||
|
if (!TypeSchemas.TryGetValue(key, out SchemaNode? value))
|
||||||
|
{
|
||||||
|
value = new SchemaNode()
|
||||||
|
{
|
||||||
|
Resolver = Resolver,
|
||||||
|
Path = key
|
||||||
|
};
|
||||||
|
TypeSchemas[key] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
return value;
|
||||||
/// Получение набора схем, к которым относится проверяемый объект.
|
}
|
||||||
/// </summary>
|
|
||||||
/// <param name="folder">Папка компендиума, в которой находится объект.</param>
|
|
||||||
/// <param name="itemType">Указанный тип объекта.</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
protected List<SchemaNode> GetItemSchemas(CompendiumFolder folder, string? itemType)
|
|
||||||
{
|
|
||||||
var key = folder.Type.ToString() + (itemType is not null ? $".{itemType}" : "");
|
|
||||||
|
|
||||||
if (_equipmentTypes.Contains(itemType))
|
/// <summary>
|
||||||
|
/// Получение набора схем, к которым относится проверяемый объект.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="folder">Папка компендиума, в которой находится объект.</param>
|
||||||
|
/// <param name="itemType">Указанный тип объекта.</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
protected List<SchemaNode> GetItemSchemas(CompendiumFolder folder, string? itemType)
|
||||||
|
{
|
||||||
|
string key = folder.Type.ToString() + (itemType is not null ? $".{itemType}" : "");
|
||||||
|
|
||||||
|
return _equipmentTypes.Contains(itemType)
|
||||||
|
? [GetPackSchema(folder), GetTypeSchema(key), GetTypeSchema("Equipment")]
|
||||||
|
: [GetPackSchema(folder), GetTypeSchema(key)];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Построение схем данных.
|
||||||
|
/// </summary>
|
||||||
|
public void BuildSchemas()
|
||||||
|
{
|
||||||
|
Console.WriteLine("Building schemas...");
|
||||||
|
|
||||||
|
ProgressBar bar = new(Loader.ItemCount);
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
foreach (CompendiumLoader.LoaderEnrty entry in Loader.Enrties())
|
||||||
|
{
|
||||||
|
bar.Refresh(i++, entry.Folder.Name);
|
||||||
|
|
||||||
|
List<SchemaNode> schemas = GetItemSchemas(entry.Folder, entry.Type);
|
||||||
|
|
||||||
|
foreach (SchemaNode schema in schemas)
|
||||||
{
|
{
|
||||||
return [GetPackSchema(folder), GetTypeSchema(key), GetTypeSchema("Equipment")];
|
schema.AddValue(entry.Json);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return [GetPackSchema(folder), GetTypeSchema(key)];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
bar.Refresh(Loader.ItemCount, "Done!");
|
||||||
/// Построение схем данных.
|
}
|
||||||
/// </summary>
|
|
||||||
public void BuildSchemas()
|
/// <summary>
|
||||||
|
/// Сохранение данных.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="targetDir">Папка для сохранения.</param>
|
||||||
|
public void Save(string targetDir)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Dumping schemas...");
|
||||||
|
|
||||||
|
ProgressBar bar = new(PackSchemas.Count + TypeSchemas.Count);
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
targetDir = Path.Combine(targetDir, Loader.Importer.Slug);
|
||||||
|
|
||||||
|
if (!Directory.Exists(targetDir))
|
||||||
{
|
{
|
||||||
Console.WriteLine("Building schemas...");
|
Directory.CreateDirectory(targetDir);
|
||||||
|
}
|
||||||
|
Array.ForEach(Directory.GetFiles(@targetDir), File.Delete);
|
||||||
|
|
||||||
var bar = new ProgressBar(Loader.ItemCount);
|
// Packs
|
||||||
var i = 0;
|
string packsDir = Path.Combine(targetDir, "packs");
|
||||||
|
Directory.CreateDirectory(packsDir);
|
||||||
|
|
||||||
foreach (var entry in Loader.Enrties())
|
foreach (KeyValuePair<CompendiumFolder, SchemaNode> pair in PackSchemas)
|
||||||
{
|
{
|
||||||
bar.Refresh(i++, entry.Folder.Name);
|
bar.Refresh(i++, pair.Key.Name);
|
||||||
|
SaveSchema(packsDir, pair.Key.Name, pair.Value);
|
||||||
var schemas = GetItemSchemas(entry.Folder, entry.Type);
|
|
||||||
|
|
||||||
foreach (var schema in schemas)
|
|
||||||
{
|
|
||||||
schema.AddValue(entry.Json);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bar.Refresh(Loader.ItemCount, "Done!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
// Types
|
||||||
/// Сохранение данных.
|
string typesDir = Path.Combine(targetDir, "types");
|
||||||
/// </summary>
|
Directory.CreateDirectory(typesDir);
|
||||||
/// <param name="targetDir">Папка для сохранения.</param>
|
foreach (KeyValuePair<string, SchemaNode> pair in TypeSchemas)
|
||||||
public void Save(string targetDir)
|
|
||||||
{
|
{
|
||||||
Console.WriteLine("Dumping schemas...");
|
bar.Refresh(i++, pair.Key);
|
||||||
|
SaveSchema(typesDir, pair.Key, pair.Value);
|
||||||
var bar = new ProgressBar(PackSchemas.Count + TypeSchemas.Count);
|
|
||||||
var i = 0;
|
|
||||||
|
|
||||||
targetDir = Path.Combine(targetDir, Loader.Importer.Slug);
|
|
||||||
|
|
||||||
if (!Directory.Exists(targetDir))
|
|
||||||
{
|
|
||||||
Directory.CreateDirectory(targetDir);
|
|
||||||
}
|
|
||||||
Array.ForEach(Directory.GetFiles(@targetDir), File.Delete);
|
|
||||||
|
|
||||||
// Packs
|
|
||||||
var packsDir = Path.Combine(targetDir, "packs");
|
|
||||||
Directory.CreateDirectory(packsDir);
|
|
||||||
|
|
||||||
foreach (var pair in PackSchemas)
|
|
||||||
{
|
|
||||||
bar.Refresh(i++, pair.Key.Name);
|
|
||||||
SaveSchema(packsDir, pair.Key.Name, pair.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Types
|
|
||||||
var typesDir = Path.Combine(targetDir, "types");
|
|
||||||
Directory.CreateDirectory(typesDir);
|
|
||||||
foreach (var pair in TypeSchemas)
|
|
||||||
{
|
|
||||||
bar.Refresh(i++, pair.Key);
|
|
||||||
SaveSchema(typesDir, pair.Key, pair.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
Console.WriteLine("Done!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
Console.WriteLine("Done!");
|
||||||
/// Сохранение конкретной схемы.
|
}
|
||||||
/// </summary>
|
|
||||||
/// <param name="dir">Корневая папка для сохранения этой категории схем.</param>
|
/// <summary>
|
||||||
/// <param name="name">Название сохраняемой схемы.</param>
|
/// Сохранение конкретной схемы.
|
||||||
/// <param name="schema">Сохраняемая схема.</param>
|
/// </summary>
|
||||||
protected void SaveSchema(string dir, string name, SchemaNode schema)
|
/// <param name="dir">Корневая папка для сохранения этой категории схем.</param>
|
||||||
|
/// <param name="name">Название сохраняемой схемы.</param>
|
||||||
|
/// <param name="schema">Сохраняемая схема.</param>
|
||||||
|
protected void SaveSchema(string dir, string name, SchemaNode schema)
|
||||||
|
{
|
||||||
|
char[] invalids = Path.GetInvalidFileNameChars();
|
||||||
|
string fileName = string.Join("_", schema.Path.Split(invalids, StringSplitOptions.RemoveEmptyEntries)).TrimEnd('.');
|
||||||
|
|
||||||
|
string path = Path.Combine(dir, fileName + ".yaml");
|
||||||
|
|
||||||
|
string? dirPath = Path.GetDirectoryName(path);
|
||||||
|
|
||||||
|
if (dirPath is not null && !Directory.Exists(dirPath))
|
||||||
{
|
{
|
||||||
var invalids = Path.GetInvalidFileNameChars();
|
Directory.CreateDirectory(dirPath);
|
||||||
var fileName = string.Join("_", schema.Path.Split(invalids, StringSplitOptions.RemoveEmptyEntries)).TrimEnd('.');
|
|
||||||
|
|
||||||
var path = Path.Combine(dir, fileName + ".yaml");
|
|
||||||
|
|
||||||
var dirPath = Path.GetDirectoryName(path);
|
|
||||||
|
|
||||||
if (dirPath is not null && !Directory.Exists(dirPath))
|
|
||||||
{
|
|
||||||
Directory.CreateDirectory(dirPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
using var writer = new StreamWriter(path);
|
|
||||||
|
|
||||||
schema.Beautify();
|
|
||||||
|
|
||||||
writer.WriteLine(SchemaNode.FormatYaml(new Dictionary<string, string>()
|
|
||||||
{
|
|
||||||
["name"] = name,
|
|
||||||
["values"] = schema.Values.ToString()
|
|
||||||
}, schema.Types[0].GetYamlData()));
|
|
||||||
|
|
||||||
//writer.WriteLine(name);
|
|
||||||
//writer.WriteLine($"Different values: {schema.Values}");
|
|
||||||
//writer.WriteLine("=====================================");
|
|
||||||
//writer.Write(schema.ToString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using StreamWriter writer = new(path);
|
||||||
|
|
||||||
|
schema.Beautify();
|
||||||
|
|
||||||
|
writer.WriteLine(SchemaNode.FormatYaml(new Dictionary<string, string>()
|
||||||
|
{
|
||||||
|
["name"] = name,
|
||||||
|
["values"] = schema.Values.ToString()
|
||||||
|
}, schema.Types[0].GetYamlData()));
|
||||||
|
|
||||||
|
//writer.WriteLine(name);
|
||||||
|
//writer.WriteLine($"Different values: {schema.Values}");
|
||||||
|
//writer.WriteLine("=====================================");
|
||||||
|
//writer.Write(schema.ToString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+43
-52
@@ -2,61 +2,52 @@
|
|||||||
using Pf2eDataScrapper.SchemaBuilder;
|
using Pf2eDataScrapper.SchemaBuilder;
|
||||||
using Pf2eDataScrapper.SchemaBuilder.Types;
|
using Pf2eDataScrapper.SchemaBuilder.Types;
|
||||||
|
|
||||||
namespace Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry.SchemaBuilder
|
namespace Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry.SchemaBuilder;
|
||||||
|
|
||||||
|
internal class CompendiumSchemaTypeResolver : TypeResolver
|
||||||
{
|
{
|
||||||
internal class CompendiumSchemaTypeResolver : TypeResolver
|
public class FoundryRulesSchemaType(SchemaNode parent) : IgnoreSchemaType(parent)
|
||||||
{
|
{
|
||||||
public class FoundryRulesSchemaType(SchemaNode parent) : IgnoreSchemaType(parent)
|
public override string GetTypeName()
|
||||||
{
|
{
|
||||||
public override string GetTypeName()
|
return "ignored (Foundry rules object)";
|
||||||
{
|
|
||||||
return "ignored (Foundry rules object)";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected readonly List<string> DictionaryPaths = [
|
|
||||||
"Actor.character.items.[].system.items",
|
|
||||||
"Actor.character.items.[].system.overlays",
|
|
||||||
"Item.ancestry.system.items",
|
|
||||||
"Item.ancestry.system.boosts",
|
|
||||||
"Item.background.system.boosts",
|
|
||||||
"Item.background.system.items",
|
|
||||||
"Item.class.system.items",
|
|
||||||
"Item.consumable.system.spell.system.damage",
|
|
||||||
"Item.consumable.system.spell.system.overlays",
|
|
||||||
"Item.deity.system.spells",
|
|
||||||
"Item.feat.system.subfeatures.proficiencies",
|
|
||||||
"Item.kit.system.items",
|
|
||||||
"Item.kit.system.items.[value].items",
|
|
||||||
"Item.spell.system.damage",
|
|
||||||
"Item.spell.system.heightening.damage",
|
|
||||||
"Item.spell.system.heightening.levels",
|
|
||||||
"Item.spell.system.heightening.levels.[value].damage",
|
|
||||||
"Item.spell.system.overlays",
|
|
||||||
"Item.spell.system.overlays.[value].system.damage",
|
|
||||||
"Item.spell.system.overlays.[value].system.heightening",
|
|
||||||
"Item.spell.system.overlays.[value].system.heightening.[value]"
|
|
||||||
];
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Частное определение типа поля.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="node">Поле схемы данных, для которого создаётся тип.</param>
|
|
||||||
/// <param name="value">Добавляемое значение.</param>
|
|
||||||
/// <returns>Новый тип для схемы данных. `null`, что бы использовать значение по умолчанию</returns>
|
|
||||||
protected override AbstractSchemaType? CustomResolve(SchemaNode node, JToken? value)
|
|
||||||
{
|
|
||||||
if (CheckPath(node, DictionaryPaths))
|
|
||||||
{
|
|
||||||
return new DictionarySchemaType(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node.Path.EndsWith(".rules"))
|
|
||||||
{
|
|
||||||
return new FoundryRulesSchemaType(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected readonly List<string> DictionaryPaths = [
|
||||||
|
"Actor.character.items.[].system.items",
|
||||||
|
"Actor.character.items.[].system.overlays",
|
||||||
|
"Item.ancestry.system.items",
|
||||||
|
"Item.ancestry.system.boosts",
|
||||||
|
"Item.background.system.boosts",
|
||||||
|
"Item.background.system.items",
|
||||||
|
"Item.class.system.items",
|
||||||
|
"Item.consumable.system.spell.system.damage",
|
||||||
|
"Item.consumable.system.spell.system.overlays",
|
||||||
|
"Item.deity.system.spells",
|
||||||
|
"Item.feat.system.subfeatures.proficiencies",
|
||||||
|
"Item.kit.system.items",
|
||||||
|
"Item.kit.system.items.[value].items",
|
||||||
|
"Item.spell.system.damage",
|
||||||
|
"Item.spell.system.heightening.damage",
|
||||||
|
"Item.spell.system.heightening.levels",
|
||||||
|
"Item.spell.system.heightening.levels.[value].damage",
|
||||||
|
"Item.spell.system.overlays",
|
||||||
|
"Item.spell.system.overlays.[value].system.damage",
|
||||||
|
"Item.spell.system.overlays.[value].system.heightening",
|
||||||
|
"Item.spell.system.overlays.[value].system.heightening.[value]"
|
||||||
|
];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Частное определение типа поля.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="node">Поле схемы данных, для которого создаётся тип.</param>
|
||||||
|
/// <param name="value">Добавляемое значение.</param>
|
||||||
|
/// <returns>Новый тип для схемы данных. `null`, что бы использовать значение по умолчанию</returns>
|
||||||
|
protected override AbstractSchemaType? CustomResolve(SchemaNode node, JToken? value)
|
||||||
|
{
|
||||||
|
return CheckPath(node, DictionaryPaths)
|
||||||
|
? new DictionarySchemaType(node)
|
||||||
|
: node.Path.EndsWith(".rules") ? new FoundryRulesSchemaType(node) : (AbstractSchemaType?) null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
namespace Pf2eDataScrapper.Importers.Repositories
|
namespace Pf2eDataScrapper.Importers.Repositories;
|
||||||
|
|
||||||
|
internal abstract class AbstractImporterRepository(AbstractImporter importer)
|
||||||
{
|
{
|
||||||
internal abstract class AbstractImporterRepository (AbstractImporter importer)
|
public readonly AbstractImporter Importer = importer;
|
||||||
{
|
|
||||||
public readonly AbstractImporter Importer = importer;
|
|
||||||
|
|
||||||
public string RepositoryDirPath => Importer.Dir;
|
public string RepositoryDirPath => Importer.Dir;
|
||||||
|
|
||||||
public DirectoryInfo RepositoryDir => new(RepositoryDirPath);
|
public DirectoryInfo RepositoryDir => new(RepositoryDirPath);
|
||||||
|
|
||||||
public abstract void Update();
|
public abstract void Update();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,52 +1,50 @@
|
|||||||
using System.Data.Common;
|
using System.Diagnostics;
|
||||||
using System.Diagnostics;
|
|
||||||
|
|
||||||
namespace Pf2eDataScrapper.Importers.Repositories
|
namespace Pf2eDataScrapper.Importers.Repositories;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Загрузчик данных из git-репозиториев
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="repositoryDirPath"></param>
|
||||||
|
internal class GitRepository(AbstractImporter importer, string url) : AbstractImporterRepository(importer)
|
||||||
{
|
{
|
||||||
|
public readonly string Url = url;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Загрузчик данных из git-репозиториев
|
/// Обновление импортёра
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="repositoryDirPath"></param>
|
public override void Update()
|
||||||
internal class GitRepository(AbstractImporter importer, string url) : AbstractImporterRepository(importer)
|
|
||||||
{
|
{
|
||||||
public readonly string Url = url;
|
string gitPath = Path.Combine(RepositoryDirPath, importer.Slug);
|
||||||
|
|
||||||
/// <summary>
|
if (Directory.Exists(gitPath))
|
||||||
/// Обновление импортёра
|
|
||||||
/// </summary>
|
|
||||||
public override void Update()
|
|
||||||
{
|
{
|
||||||
var gitPath = Path.Combine(RepositoryDirPath, importer.Slug);
|
// git pull
|
||||||
|
Process process = new()
|
||||||
if (Directory.Exists(gitPath))
|
|
||||||
{
|
{
|
||||||
// git pull
|
StartInfo = new ProcessStartInfo()
|
||||||
var process = new Process
|
|
||||||
{
|
{
|
||||||
StartInfo = new ProcessStartInfo()
|
FileName = "git",
|
||||||
{
|
WorkingDirectory = gitPath,
|
||||||
FileName = "git",
|
Arguments = "pull",
|
||||||
WorkingDirectory = gitPath,
|
}
|
||||||
Arguments = "pull",
|
};
|
||||||
}
|
process.Start();
|
||||||
};
|
}
|
||||||
process.Start();
|
else
|
||||||
}
|
{
|
||||||
else
|
// git clone
|
||||||
|
Process process = new()
|
||||||
{
|
{
|
||||||
// git clone
|
StartInfo = new ProcessStartInfo()
|
||||||
var process = new Process
|
|
||||||
{
|
{
|
||||||
StartInfo = new ProcessStartInfo()
|
FileName = "git",
|
||||||
{
|
WorkingDirectory = RepositoryDirPath,
|
||||||
FileName = "git",
|
Arguments = $"clone {Url} {Importer.Slug}",
|
||||||
WorkingDirectory = RepositoryDirPath,
|
}
|
||||||
Arguments = $"clone {Url} {Importer.Slug}",
|
};
|
||||||
}
|
process.Start();
|
||||||
};
|
|
||||||
process.Start();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using Pf2eDataScrapper.Model.Enum;
|
using Pf2eDataScrapper.Model.Common;
|
||||||
|
|
||||||
namespace Pf2eDataScrapper.Model;
|
namespace Pf2eDataScrapper.Model;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using Pf2eDataScrapper.Model.Enum;
|
using Pf2eDataScrapper.Model.Common;
|
||||||
|
|
||||||
namespace Pf2eDataScrapper.Model;
|
namespace Pf2eDataScrapper.Model;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
namespace Pf2eDataScrapper.Model;
|
using Pf2eDataScrapper.Model.Common;
|
||||||
|
|
||||||
|
namespace Pf2eDataScrapper.Model;
|
||||||
|
|
||||||
public partial class AncestryItem
|
public partial class AncestryItem
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using Pf2eDataScrapper.Model.Enum;
|
using Pf2eDataScrapper.Model.Common;
|
||||||
|
|
||||||
namespace Pf2eDataScrapper.Model;
|
namespace Pf2eDataScrapper.Model;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
namespace Pf2eDataScrapper.Model;
|
using Pf2eDataScrapper.Model.Common;
|
||||||
|
|
||||||
|
namespace Pf2eDataScrapper.Model;
|
||||||
|
|
||||||
public partial class BackgroundItem
|
public partial class BackgroundItem
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using Pf2eDataScrapper.Model.Enum;
|
using Pf2eDataScrapper.Model.Common;
|
||||||
|
|
||||||
namespace Pf2eDataScrapper.Model;
|
namespace Pf2eDataScrapper.Model;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using Pf2eDataScrapper.Model.Enum;
|
using Pf2eDataScrapper.Model.Common;
|
||||||
|
|
||||||
namespace Pf2eDataScrapper.Model;
|
namespace Pf2eDataScrapper.Model;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
namespace Pf2eDataScrapper.Model;
|
using Pf2eDataScrapper.Model.Common;
|
||||||
|
|
||||||
|
namespace Pf2eDataScrapper.Model;
|
||||||
|
|
||||||
public partial class ClassProgression
|
public partial class ClassProgression
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Pf2eDataScrapper.Model.Enum;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Pf2eDataScrapper.Model.Common;
|
||||||
|
|
||||||
|
[Table("entity")]
|
||||||
|
public class Entity
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
[Column("id", Order = 1)]
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
[Column("name", Order = 2)]
|
||||||
|
[StringLength(255)]
|
||||||
|
[Required]
|
||||||
|
[Comment("Отображаемое имя")]
|
||||||
|
public string Name { get; set; } = null!;
|
||||||
|
|
||||||
|
[Column("type", Order = 3)]
|
||||||
|
[Required]
|
||||||
|
[Comment("Тип объекта")]
|
||||||
|
public EntityType Type { get; set; }
|
||||||
|
|
||||||
|
[Column("slug", Order = 4)]
|
||||||
|
[Required]
|
||||||
|
[Comment("Часть имени в URL")]
|
||||||
|
public string Slug { get; set; } = null!;
|
||||||
|
|
||||||
|
[Column("img", Order = 5)]
|
||||||
|
[StringLength(255)]
|
||||||
|
[Comment("Изображение объекта")]
|
||||||
|
public string Img { get; set; } = null!;
|
||||||
|
|
||||||
|
[Column("description")]
|
||||||
|
[Comment("Текстовое описание объекта")]
|
||||||
|
public string Description { get; set; } = null!;
|
||||||
|
|
||||||
|
[Column("rarity")]
|
||||||
|
public EntityRarity Rarity { get; set; } = EntityRarity.Common;
|
||||||
|
|
||||||
|
[Column("is_translated")]
|
||||||
|
[Comment("Флаг, указывающий, что у объекта существует перевод")]
|
||||||
|
public bool? IsTranslated { get; set; } = false;
|
||||||
|
|
||||||
|
[Column("is_checked")]
|
||||||
|
[Comment("Флаг, указывающий, что объект был проверен и не требует автоматической перезаписи")]
|
||||||
|
public bool? IsChecked { get; set; } = false;
|
||||||
|
|
||||||
|
public virtual ICollection<Trait> Traits { get; set; } = [];
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
using Pf2eDataScrapper.Model.Enum;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Pf2eDataScrapper.Model.Common;
|
||||||
|
|
||||||
|
[Table("entity_alias")]
|
||||||
|
public partial class EntityAlias
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
[Column("id", Order = 1)]
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
[Column("entity_id", Order = 2)]
|
||||||
|
[ForeignKey("Entity")]
|
||||||
|
public int EntityId { get; set; }
|
||||||
|
|
||||||
|
[Column("type", Order = 3)]
|
||||||
|
public AliasType Type { get; set; } = AliasType.Other;
|
||||||
|
|
||||||
|
[Column("alias", Order = 4)]
|
||||||
|
public string Alias { get; set; } = null!;
|
||||||
|
|
||||||
|
public virtual Entity Entity { get; set; } = null!;
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
using Pf2eDataScrapper.Model.Enum;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Pf2eDataScrapper.Model.Common;
|
||||||
|
|
||||||
|
[Table("trait")]
|
||||||
|
public partial class Trait
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
[Column("id", Order = 1)]
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
[Column("name", Order = 2)]
|
||||||
|
[Required]
|
||||||
|
public string Name { get; set; } = null!;
|
||||||
|
|
||||||
|
[Column("type", Order = 3)]
|
||||||
|
public TraitType Type { get; set; }
|
||||||
|
|
||||||
|
[Column("slug", Order = 4)]
|
||||||
|
[Required]
|
||||||
|
public string Slug { get; set; } = null!;
|
||||||
|
|
||||||
|
[Column("description", Order = 5)]
|
||||||
|
public string Description { get; set; } = null!;
|
||||||
|
|
||||||
|
public virtual ICollection<Entity> Entities { get; set; } = [];
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using Pf2eDataScrapper.Model.Enum;
|
using Pf2eDataScrapper.Model.Common;
|
||||||
|
using Pf2eDataScrapper.Model.Enum;
|
||||||
|
|
||||||
namespace Pf2eDataScrapper.Model;
|
namespace Pf2eDataScrapper.Model;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using Pf2eDataScrapper.Model.Enum;
|
using Pf2eDataScrapper.Model.Common;
|
||||||
|
|
||||||
namespace Pf2eDataScrapper.Model;
|
namespace Pf2eDataScrapper.Model;
|
||||||
|
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
using Pf2eDataScrapper.Model.Enum;
|
|
||||||
|
|
||||||
namespace Pf2eDataScrapper.Model;
|
|
||||||
|
|
||||||
public class Entity
|
|
||||||
{
|
|
||||||
public int Id { get; set; }
|
|
||||||
|
|
||||||
public string Name { get; set; } = null!;
|
|
||||||
|
|
||||||
public string Img { get; set; } = null!;
|
|
||||||
|
|
||||||
public EntityType Type { get; set; }
|
|
||||||
|
|
||||||
public string Description { get; set; } = null!;
|
|
||||||
|
|
||||||
public string? DescriptionGm { get; set; }
|
|
||||||
|
|
||||||
public string Slug { get; set; } = null!;
|
|
||||||
|
|
||||||
public bool? IsTranslated { get; set; }
|
|
||||||
|
|
||||||
public bool? IsChecked { get; set; }
|
|
||||||
|
|
||||||
public virtual ICollection<Trait> Traits { get; set; } = [];
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
namespace Pf2eDataScrapper.Model;
|
|
||||||
|
|
||||||
public partial class EntityAlias
|
|
||||||
{
|
|
||||||
public int Id { get; set; }
|
|
||||||
|
|
||||||
public int EntityId { get; set; }
|
|
||||||
|
|
||||||
public string Alias { get; set; } = null!;
|
|
||||||
|
|
||||||
public virtual Entity Entity { get; set; } = null!;
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
namespace Pf2eDataScrapper.Model;
|
using Pf2eDataScrapper.Model.Common;
|
||||||
|
|
||||||
|
namespace Pf2eDataScrapper.Model;
|
||||||
|
|
||||||
public partial class EntityExternal
|
public partial class EntityExternal
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
namespace Pf2eDataScrapper.Model;
|
using Pf2eDataScrapper.Model.Common;
|
||||||
|
|
||||||
|
namespace Pf2eDataScrapper.Model;
|
||||||
|
|
||||||
public partial class EntityKeyword
|
public partial class EntityKeyword
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
namespace Pf2eDataScrapper.Model;
|
using Pf2eDataScrapper.Model.Common;
|
||||||
|
|
||||||
|
namespace Pf2eDataScrapper.Model;
|
||||||
|
|
||||||
public partial class EntitySource
|
public partial class EntitySource
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
using NpgsqlTypes;
|
||||||
|
|
||||||
|
namespace Pf2eDataScrapper.Model.Enum;
|
||||||
|
|
||||||
|
public enum ActionCategory
|
||||||
|
{
|
||||||
|
[PgName("offensive")]
|
||||||
|
offensive,
|
||||||
|
|
||||||
|
[PgName("defensive")]
|
||||||
|
defensive,
|
||||||
|
|
||||||
|
[PgName("interaction")]
|
||||||
|
interaction,
|
||||||
|
|
||||||
|
[PgName("familiar")]
|
||||||
|
familiar,
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
using NpgsqlTypes;
|
||||||
|
|
||||||
|
namespace Pf2eDataScrapper.Model.Enum;
|
||||||
|
|
||||||
|
public enum ActionType
|
||||||
|
{
|
||||||
|
[PgName("action")]
|
||||||
|
action,
|
||||||
|
|
||||||
|
[PgName("free")]
|
||||||
|
free,
|
||||||
|
|
||||||
|
[PgName("reaction")]
|
||||||
|
reaction,
|
||||||
|
|
||||||
|
[PgName("passive")]
|
||||||
|
passive,
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
using NpgsqlTypes;
|
||||||
|
|
||||||
|
namespace Pf2eDataScrapper.Model.Enum;
|
||||||
|
|
||||||
|
public enum AliasType
|
||||||
|
{
|
||||||
|
[PgName("old")]
|
||||||
|
Old,
|
||||||
|
|
||||||
|
[PgName("english")]
|
||||||
|
English,
|
||||||
|
|
||||||
|
[PgName("old_english")]
|
||||||
|
OldEnglish,
|
||||||
|
|
||||||
|
[PgName("hobby")]
|
||||||
|
Hobby,
|
||||||
|
|
||||||
|
[PgName("other")]
|
||||||
|
Other,
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
using NpgsqlTypes;
|
||||||
|
|
||||||
|
namespace Pf2eDataScrapper.Model.Enum;
|
||||||
|
|
||||||
|
public enum AncestryVision
|
||||||
|
{
|
||||||
|
[PgName("normal")]
|
||||||
|
normal,
|
||||||
|
|
||||||
|
[PgName("low-light-vision")]
|
||||||
|
LowLightVision,
|
||||||
|
|
||||||
|
[PgName("darkvision")]
|
||||||
|
darkvision,
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
using NpgsqlTypes;
|
||||||
|
|
||||||
|
namespace Pf2eDataScrapper.Model.Enum;
|
||||||
|
|
||||||
|
public enum CampaignFeatureCategory
|
||||||
|
{
|
||||||
|
[PgName("army_tactic")]
|
||||||
|
ArmyTactic,
|
||||||
|
|
||||||
|
[PgName("army_war_action")]
|
||||||
|
ArmyWarAction,
|
||||||
|
|
||||||
|
[PgName("kingdom_activity")]
|
||||||
|
KingdomActivity,
|
||||||
|
|
||||||
|
[PgName("kingdom_feat")]
|
||||||
|
KingdomFeat,
|
||||||
|
|
||||||
|
[PgName("kingdom_feature")]
|
||||||
|
KingdomFeature,
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
using NpgsqlTypes;
|
||||||
|
|
||||||
|
namespace Pf2eDataScrapper.Model.Enum;
|
||||||
|
|
||||||
|
public enum ClassProgressionType
|
||||||
|
{
|
||||||
|
[PgName("entity")]
|
||||||
|
Entity,
|
||||||
|
|
||||||
|
[PgName("ancestry_feat")]
|
||||||
|
AncestryFeat,
|
||||||
|
|
||||||
|
[PgName("class_feat")]
|
||||||
|
ClassFeat,
|
||||||
|
|
||||||
|
[PgName("general_feat")]
|
||||||
|
GeneralFeat,
|
||||||
|
|
||||||
|
[PgName("skill_feat")]
|
||||||
|
SkillFeat,
|
||||||
|
|
||||||
|
[PgName("skill_increase")]
|
||||||
|
SkillIncrease,
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
using NpgsqlTypes;
|
||||||
|
|
||||||
|
namespace Pf2eDataScrapper.Model.Enum;
|
||||||
|
|
||||||
|
public enum DeityCategory
|
||||||
|
{
|
||||||
|
[PgName("deity")]
|
||||||
|
Deity,
|
||||||
|
|
||||||
|
[PgName("pantheon")]
|
||||||
|
Pantheon,
|
||||||
|
|
||||||
|
[PgName("covenant")]
|
||||||
|
Covenant,
|
||||||
|
|
||||||
|
[PgName("philosophy")]
|
||||||
|
Philosophy,
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
using NpgsqlTypes;
|
||||||
|
|
||||||
|
namespace Pf2eDataScrapper.Model.Enum;
|
||||||
|
|
||||||
|
public enum DeityFonts
|
||||||
|
{
|
||||||
|
[PgName("none")]
|
||||||
|
None,
|
||||||
|
|
||||||
|
[PgName("heal")]
|
||||||
|
Heal,
|
||||||
|
|
||||||
|
[PgName("harm")]
|
||||||
|
Harm,
|
||||||
|
|
||||||
|
[PgName("any")]
|
||||||
|
Any,
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
using NpgsqlTypes;
|
||||||
|
|
||||||
|
namespace Pf2eDataScrapper.Model.Enum;
|
||||||
|
|
||||||
|
public enum DeitySanctification
|
||||||
|
{
|
||||||
|
[PgName("holy")]
|
||||||
|
Holy,
|
||||||
|
|
||||||
|
[PgName("unholy")]
|
||||||
|
Unholy,
|
||||||
|
|
||||||
|
[PgName("any")]
|
||||||
|
Any,
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
using NpgsqlTypes;
|
||||||
|
|
||||||
|
namespace Pf2eDataScrapper.Model.Enum;
|
||||||
|
|
||||||
|
public enum EntityRarity
|
||||||
|
{
|
||||||
|
[PgName("common")]
|
||||||
|
Common,
|
||||||
|
|
||||||
|
[PgName("uncommon")]
|
||||||
|
Uncommon,
|
||||||
|
|
||||||
|
[PgName("rare")]
|
||||||
|
Rare,
|
||||||
|
|
||||||
|
[PgName("unique")]
|
||||||
|
Unique
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
using NpgsqlTypes;
|
||||||
|
|
||||||
|
namespace Pf2eDataScrapper.Model.Enum;
|
||||||
|
|
||||||
|
public enum FeatActionType
|
||||||
|
{
|
||||||
|
[PgName("passive")]
|
||||||
|
Passive,
|
||||||
|
|
||||||
|
[PgName("action")]
|
||||||
|
Action,
|
||||||
|
|
||||||
|
[PgName("reaction")]
|
||||||
|
Reaction,
|
||||||
|
|
||||||
|
[PgName("free")]
|
||||||
|
Free,
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
using NpgsqlTypes;
|
||||||
|
|
||||||
|
namespace Pf2eDataScrapper.Model.Enum;
|
||||||
|
|
||||||
|
public enum FeatCategory
|
||||||
|
{
|
||||||
|
[PgName("ancestryfeature")]
|
||||||
|
Ancestryfeature,
|
||||||
|
|
||||||
|
[PgName("classfeature")]
|
||||||
|
Classfeature,
|
||||||
|
|
||||||
|
[PgName("calling")]
|
||||||
|
Calling,
|
||||||
|
|
||||||
|
[PgName("class")]
|
||||||
|
Class,
|
||||||
|
|
||||||
|
[PgName("skill")]
|
||||||
|
Skill,
|
||||||
|
|
||||||
|
[PgName("general")]
|
||||||
|
General,
|
||||||
|
|
||||||
|
[PgName("ancestry")]
|
||||||
|
Ancestry,
|
||||||
|
|
||||||
|
[PgName("bonus")]
|
||||||
|
Bonus,
|
||||||
|
|
||||||
|
[PgName("deityboon")]
|
||||||
|
Deityboon,
|
||||||
|
|
||||||
|
[PgName("curse")]
|
||||||
|
Curse,
|
||||||
|
|
||||||
|
[PgName("pfsboon")]
|
||||||
|
Pfsboon,
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
using NpgsqlTypes;
|
||||||
|
|
||||||
|
namespace Pf2eDataScrapper.Model.Enum;
|
||||||
|
|
||||||
|
public enum Size
|
||||||
|
{
|
||||||
|
[PgName("med")]
|
||||||
|
med,
|
||||||
|
|
||||||
|
[PgName("tiny")]
|
||||||
|
tiny,
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
using NpgsqlTypes;
|
||||||
|
|
||||||
|
namespace Pf2eDataScrapper.Model.Enum;
|
||||||
|
|
||||||
|
public enum SpellAreaType
|
||||||
|
{
|
||||||
|
[PgName("burst")]
|
||||||
|
Burst,
|
||||||
|
|
||||||
|
[PgName("emanation")]
|
||||||
|
Emanation,
|
||||||
|
|
||||||
|
[PgName("cone")]
|
||||||
|
Cone,
|
||||||
|
|
||||||
|
[PgName("line")]
|
||||||
|
Line,
|
||||||
|
|
||||||
|
[PgName("square")]
|
||||||
|
Square,
|
||||||
|
|
||||||
|
[PgName("cube")]
|
||||||
|
Cube,
|
||||||
|
|
||||||
|
[PgName("cylinder")]
|
||||||
|
Cylinder,
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
using NpgsqlTypes;
|
||||||
|
|
||||||
|
namespace Pf2eDataScrapper.Model.Enum;
|
||||||
|
|
||||||
|
public enum SpellDamageCategory
|
||||||
|
{
|
||||||
|
[PgName("persistent")]
|
||||||
|
Persistent,
|
||||||
|
|
||||||
|
[PgName("splash")]
|
||||||
|
Splash,
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
using NpgsqlTypes;
|
||||||
|
|
||||||
|
namespace Pf2eDataScrapper.Model.Enum;
|
||||||
|
|
||||||
|
public enum SpellDamageKinds
|
||||||
|
{
|
||||||
|
[PgName("damage")]
|
||||||
|
Damage,
|
||||||
|
|
||||||
|
[PgName("healing")]
|
||||||
|
Healing,
|
||||||
|
|
||||||
|
[PgName("any")]
|
||||||
|
Any,
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
using NpgsqlTypes;
|
||||||
|
|
||||||
|
namespace Pf2eDataScrapper.Model.Enum;
|
||||||
|
|
||||||
|
public enum TraitType
|
||||||
|
{
|
||||||
|
[PgName("regular")]
|
||||||
|
Regular,
|
||||||
|
|
||||||
|
[PgName("tradition")]
|
||||||
|
Tradition
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
using Pf2eDataScrapper.Model.Enum;
|
using Pf2eDataScrapper.Model.Common;
|
||||||
|
|
||||||
namespace Pf2eDataScrapper.Model;
|
namespace Pf2eDataScrapper.Model;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
namespace Pf2eDataScrapper.Model;
|
using Pf2eDataScrapper.Model.Common;
|
||||||
|
|
||||||
|
namespace Pf2eDataScrapper.Model;
|
||||||
|
|
||||||
public partial class Frequency
|
public partial class Frequency
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using Pf2eDataScrapper.Model.Enum;
|
using Pf2eDataScrapper.Model.Common;
|
||||||
|
|
||||||
namespace Pf2eDataScrapper.Model;
|
namespace Pf2eDataScrapper.Model;
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
namespace Pf2eDataScrapper.Model
|
using Pf2eDataScrapper.Model.Common;
|
||||||
|
|
||||||
|
namespace Pf2eDataScrapper.Model;
|
||||||
|
|
||||||
|
internal interface IEntity
|
||||||
{
|
{
|
||||||
internal interface IEntity
|
public int Id { get; set; }
|
||||||
{
|
|
||||||
public int Id { get; set; }
|
|
||||||
|
|
||||||
public Entity Entity { get; set; }
|
public Entity Entity { get; set; }
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using Pf2eDataScrapper.Model.Enum;
|
using Pf2eDataScrapper.Model.Common;
|
||||||
|
|
||||||
namespace Pf2eDataScrapper.Model;
|
namespace Pf2eDataScrapper.Model;
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
namespace Pf2eDataScrapper.Model;
|
|
||||||
|
|
||||||
public partial class Trait
|
|
||||||
{
|
|
||||||
public int Id { get; set; }
|
|
||||||
|
|
||||||
public string Name { get; set; } = null!;
|
|
||||||
|
|
||||||
public string Slug { get; set; } = null!;
|
|
||||||
|
|
||||||
public string Description { get; set; } = null!;
|
|
||||||
}
|
|
||||||
+114
-115
@@ -1,123 +1,122 @@
|
|||||||
using Npgsql;
|
using Npgsql;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
namespace Pf2eDataScrapper
|
namespace Pf2eDataScrapper;
|
||||||
|
|
||||||
|
public partial class Pf2eDatabase
|
||||||
{
|
{
|
||||||
public partial class Pf2eDatabase
|
public NpgsqlConnection Connection;
|
||||||
|
|
||||||
|
public readonly string Username;
|
||||||
|
public readonly string DatabaseName;
|
||||||
|
|
||||||
|
|
||||||
|
private Pf2eDatabase()
|
||||||
{
|
{
|
||||||
public NpgsqlConnection Connection;
|
Username = UsernamePattern().Match(EnvSettings.DatabaseConnection).Groups[1].Value;
|
||||||
|
DatabaseName = DatabaseNamePattern().Match(EnvSettings.DatabaseConnection).Groups[1].Value;
|
||||||
|
|
||||||
public readonly string Username;
|
Connection = new NpgsqlConnection(EnvSettings.DatabaseConnection);
|
||||||
public readonly string DatabaseName;
|
Connection.Open();
|
||||||
|
|
||||||
|
|
||||||
private Pf2eDatabase()
|
|
||||||
{
|
|
||||||
Username = UsernamePattern().Match(EnvSettings.DatabaseConnection).Groups[1].Value;
|
|
||||||
DatabaseName = DatabaseNamePattern().Match(EnvSettings.DatabaseConnection).Groups[1].Value;
|
|
||||||
|
|
||||||
Connection = new NpgsqlConnection(EnvSettings.DatabaseConnection);
|
|
||||||
Connection.Open();
|
|
||||||
}
|
|
||||||
|
|
||||||
~Pf2eDatabase()
|
|
||||||
{
|
|
||||||
Connection.Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Pf2eDatabase Instance { get; } = new();
|
|
||||||
|
|
||||||
public NpgsqlTransaction? Transaction { get; private set; }
|
|
||||||
|
|
||||||
public void StartTransaction()
|
|
||||||
{
|
|
||||||
if (Transaction != null)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("Transaction already started.");
|
|
||||||
}
|
|
||||||
|
|
||||||
Transaction = Connection.BeginTransaction();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Commit()
|
|
||||||
{
|
|
||||||
if (Transaction == null)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("No transaction started.");
|
|
||||||
}
|
|
||||||
|
|
||||||
Transaction.Commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ExecuteCommand(string sql)
|
|
||||||
{
|
|
||||||
var command = GetCommand(sql);
|
|
||||||
|
|
||||||
command.ExecuteNonQuery();
|
|
||||||
}
|
|
||||||
|
|
||||||
public NpgsqlCommand GetCommand(string sql)
|
|
||||||
{
|
|
||||||
return new NpgsqlCommand(sql, Connection);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
public void RebuildDatabase()
|
|
||||||
{
|
|
||||||
// Закрываем текущее соединение
|
|
||||||
Connection.Close();
|
|
||||||
|
|
||||||
// Получение имён
|
|
||||||
var databaseNamePattern = DatabaseNamePattern();
|
|
||||||
var userConnection = databaseNamePattern.Replace(EnvSettings.DatabaseConnection, ";");
|
|
||||||
var databaseName = databaseNamePattern.Match(EnvSettings.DatabaseConnection).Groups[1].Value;
|
|
||||||
var username = UserNamePattern().Match(EnvSettings.DatabaseConnection).Groups[1].Value;
|
|
||||||
|
|
||||||
// Временное подключение к базе данных
|
|
||||||
NpgsqlConnection tmpConnection = new(userConnection);
|
|
||||||
tmpConnection.Open();
|
|
||||||
|
|
||||||
// Удаляем базу данных
|
|
||||||
NpgsqlCommand dropCommand = new($"DROP DATABASE \"{databaseName}\";", tmpConnection);
|
|
||||||
dropCommand.ExecuteNonQuery();
|
|
||||||
|
|
||||||
// Создаём новую базу данных
|
|
||||||
NpgsqlCommand createCommand = new($@"
|
|
||||||
CREATE DATABASE {databaseName}
|
|
||||||
WITH OWNER = {username}
|
|
||||||
ENCODING = 'UTF8'
|
|
||||||
CONNECTION LIMIT = -1;
|
|
||||||
", tmpConnection);
|
|
||||||
createCommand.ExecuteNonQuery();
|
|
||||||
|
|
||||||
string[] permissionCommands = [
|
|
||||||
$"GRANT TEMPORARY, CONNECT ON DATABASE {databaseName} TO PUBLIC;",
|
|
||||||
$"GRANT ALL ON DATABASE {databaseName} TO {username} WITH GRANT OPTION;",
|
|
||||||
$"GRANT ALL ON DATABASE {databaseName} TO postgres;"
|
|
||||||
];
|
|
||||||
|
|
||||||
foreach (var command in permissionCommands)
|
|
||||||
{
|
|
||||||
NpgsqlCommand permissionCommand = new(command, tmpConnection);
|
|
||||||
permissionCommand.ExecuteNonQuery();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Закрываем временное соединение
|
|
||||||
tmpConnection.Close();
|
|
||||||
|
|
||||||
// Заново создаём соединение
|
|
||||||
Connection = new NpgsqlConnection(EnvSettings.DatabaseConnection);
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
[GeneratedRegex(@"(?:^|;)Database=(.*?)(?:$|;)")]
|
|
||||||
private static partial Regex DatabaseNamePattern();
|
|
||||||
|
|
||||||
[GeneratedRegex(@"(?:^|;)User=(.*?)(?:$|;)")]
|
|
||||||
private static partial Regex UsernamePattern();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
~Pf2eDatabase()
|
||||||
|
{
|
||||||
|
Connection.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Pf2eDatabase Instance { get; } = new();
|
||||||
|
|
||||||
|
public NpgsqlTransaction? Transaction { get; private set; }
|
||||||
|
|
||||||
|
public void StartTransaction()
|
||||||
|
{
|
||||||
|
if (Transaction != null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Transaction already started.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Transaction = Connection.BeginTransaction();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Commit()
|
||||||
|
{
|
||||||
|
if (Transaction == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("No transaction started.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Transaction.Commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ExecuteCommand(string sql)
|
||||||
|
{
|
||||||
|
NpgsqlCommand command = GetCommand(sql);
|
||||||
|
|
||||||
|
command.ExecuteNonQuery();
|
||||||
|
}
|
||||||
|
|
||||||
|
public NpgsqlCommand GetCommand(string sql)
|
||||||
|
{
|
||||||
|
return new NpgsqlCommand(sql, Connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
public void RebuildDatabase()
|
||||||
|
{
|
||||||
|
// Закрываем текущее соединение
|
||||||
|
Connection.Close();
|
||||||
|
|
||||||
|
// Получение имён
|
||||||
|
var databaseNamePattern = DatabaseNamePattern();
|
||||||
|
var userConnection = databaseNamePattern.Replace(EnvSettings.DatabaseConnection, ";");
|
||||||
|
var databaseName = databaseNamePattern.Match(EnvSettings.DatabaseConnection).Groups[1].Value;
|
||||||
|
var username = UserNamePattern().Match(EnvSettings.DatabaseConnection).Groups[1].Value;
|
||||||
|
|
||||||
|
// Временное подключение к базе данных
|
||||||
|
NpgsqlConnection tmpConnection = new(userConnection);
|
||||||
|
tmpConnection.Open();
|
||||||
|
|
||||||
|
// Удаляем базу данных
|
||||||
|
NpgsqlCommand dropCommand = new($"DROP DATABASE \"{databaseName}\";", tmpConnection);
|
||||||
|
dropCommand.ExecuteNonQuery();
|
||||||
|
|
||||||
|
// Создаём новую базу данных
|
||||||
|
NpgsqlCommand createCommand = new($@"
|
||||||
|
CREATE DATABASE {databaseName}
|
||||||
|
WITH OWNER = {username}
|
||||||
|
ENCODING = 'UTF8'
|
||||||
|
CONNECTION LIMIT = -1;
|
||||||
|
", tmpConnection);
|
||||||
|
createCommand.ExecuteNonQuery();
|
||||||
|
|
||||||
|
string[] permissionCommands = [
|
||||||
|
$"GRANT TEMPORARY, CONNECT ON DATABASE {databaseName} TO PUBLIC;",
|
||||||
|
$"GRANT ALL ON DATABASE {databaseName} TO {username} WITH GRANT OPTION;",
|
||||||
|
$"GRANT ALL ON DATABASE {databaseName} TO postgres;"
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach (var command in permissionCommands)
|
||||||
|
{
|
||||||
|
NpgsqlCommand permissionCommand = new(command, tmpConnection);
|
||||||
|
permissionCommand.ExecuteNonQuery();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Закрываем временное соединение
|
||||||
|
tmpConnection.Close();
|
||||||
|
|
||||||
|
// Заново создаём соединение
|
||||||
|
Connection = new NpgsqlConnection(EnvSettings.DatabaseConnection);
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
[GeneratedRegex(@"(?:^|;)Database=(.*?)(?:$|;)")]
|
||||||
|
private static partial Regex DatabaseNamePattern();
|
||||||
|
|
||||||
|
[GeneratedRegex(@"(?:^|;)User=(.*?)(?:$|;)")]
|
||||||
|
private static partial Regex UsernamePattern();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
|
||||||
using Pf2eDataScrapper.Model;
|
using Pf2eDataScrapper.Model;
|
||||||
|
using Pf2eDataScrapper.Model.Common;
|
||||||
using Pf2eDataScrapper.Model.Enum;
|
using Pf2eDataScrapper.Model.Enum;
|
||||||
using System.ComponentModel;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace Pf2eDataScrapper;
|
namespace Pf2eDataScrapper;
|
||||||
|
|
||||||
@@ -103,8 +101,10 @@ public partial class Pf2eTestContext : DbContext
|
|||||||
public virtual DbSet<Trait> Traits { get; set; }
|
public virtual DbSet<Trait> Traits { get; set; }
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||||
{
|
{
|
||||||
|
EnvSettings.Load();
|
||||||
optionsBuilder.UseNpgsql(EnvSettings.DatabaseConnection,
|
optionsBuilder.UseNpgsql(EnvSettings.DatabaseConnection,
|
||||||
builder =>
|
builder =>
|
||||||
{
|
{
|
||||||
@@ -115,50 +115,6 @@ public partial class Pf2eTestContext : DbContext
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected string FromEnum<T>(T enumValue) where T : Enum
|
|
||||||
{
|
|
||||||
var enumFieldName = enumValue.ToString();
|
|
||||||
var field = typeof(T).GetField(enumFieldName);
|
|
||||||
if (field is null)
|
|
||||||
{
|
|
||||||
return enumFieldName;
|
|
||||||
}
|
|
||||||
|
|
||||||
var attribute = field.GetCustomAttribute<DescriptionAttribute>();
|
|
||||||
return attribute is null ? enumFieldName : attribute.Description;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected T ToEnum<T>(string enumValue) where T : Enum
|
|
||||||
{
|
|
||||||
var enumType = typeof(T);
|
|
||||||
foreach (var field in enumType.GetFields())
|
|
||||||
{
|
|
||||||
var attribute = field.GetCustomAttribute<DescriptionAttribute>();
|
|
||||||
if (attribute is null)
|
|
||||||
{
|
|
||||||
if (field.Name == enumValue)
|
|
||||||
{
|
|
||||||
return (T)field.GetValue(null)!;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (attribute.Description == enumValue)
|
|
||||||
{
|
|
||||||
return (T)field.GetValue(null)!;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new ArgumentException($"No matching value found for {enumValue} in {enumType.Name}");
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ValueConverter<T, string> GetEnumConverter<T>() where T : Enum
|
|
||||||
{
|
|
||||||
return new ValueConverter<T, string>(
|
|
||||||
v => FromEnum(v),
|
|
||||||
v => ToEnum<T>(v));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||||
{
|
{
|
||||||
modelBuilder.UseIdentityByDefaultColumns();
|
modelBuilder.UseIdentityByDefaultColumns();
|
||||||
@@ -198,8 +154,7 @@ public partial class Pf2eTestContext : DbContext
|
|||||||
entity.Property(e => e.Id).HasColumnName("id");
|
entity.Property(e => e.Id).HasColumnName("id");
|
||||||
entity.Property(e => e.Description).HasColumnName("description");
|
entity.Property(e => e.Description).HasColumnName("description");
|
||||||
entity.Property(e => e.DescriptionGm).HasColumnName("description_gm");
|
entity.Property(e => e.DescriptionGm).HasColumnName("description_gm");
|
||||||
entity.Property(e => e.Type)
|
entity.Property(e => e.Type).HasColumnName("type");
|
||||||
.HasColumnName("type");
|
|
||||||
entity.Property(e => e.Img)
|
entity.Property(e => e.Img)
|
||||||
.HasMaxLength(150)
|
.HasMaxLength(150)
|
||||||
.HasColumnName("img");
|
.HasColumnName("img");
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
using Pf2eDataScrapper;
|
using Pf2eDataScrapper;
|
||||||
using Pf2eDataScrapper.Importers.Implementations.Aonprd;
|
using Pf2eDataScrapper.Importers.Implementations.Aonprd;
|
||||||
using Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry;
|
|
||||||
|
|
||||||
EnvSettings.Load();
|
EnvSettings.Load();
|
||||||
|
|
||||||
var importer = new AonprdImporter();
|
AonprdImporter importer = new AonprdImporter();
|
||||||
importer.Prepare();
|
importer.Prepare();
|
||||||
importer.BuildSchemas();
|
importer.BuildSchemas();
|
||||||
|
|
||||||
|
|||||||
@@ -1,244 +1,243 @@
|
|||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using Pf2eDataScrapper.SchemaBuilder.Types;
|
using Pf2eDataScrapper.SchemaBuilder.Types;
|
||||||
|
|
||||||
namespace Pf2eDataScrapper.SchemaBuilder
|
namespace Pf2eDataScrapper.SchemaBuilder;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Элемент схемы данных.
|
||||||
|
/// </summary>
|
||||||
|
internal class SchemaNode
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Элемент схемы данных.
|
/// Отображаемое имя элемента.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal class SchemaNode
|
public string Name = "";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Путь к элементу.
|
||||||
|
/// </summary>
|
||||||
|
public string Path = "";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Родительский элемент.
|
||||||
|
/// </summary>
|
||||||
|
public SchemaNode? Parent = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Указание, является ли элемент корневым.
|
||||||
|
/// </summary>
|
||||||
|
public bool Root = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Корневой элемент схемы данных.
|
||||||
|
/// </summary>
|
||||||
|
public SchemaNode RootNode => Root ? this : Parent!.RootNode;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Типы данных, использующиеся в элементе.
|
||||||
|
/// </summary>
|
||||||
|
public List<AbstractSchemaType> Types = [];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Указание, может ли этот элемент отсутсвовать в структуре данных.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsNullable = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Объект для определения типа поля в схеме данных.
|
||||||
|
/// </summary>
|
||||||
|
public TypeResolver Resolver = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Количество значений, добавленных в тип.
|
||||||
|
/// </summary>
|
||||||
|
public int Values { get; protected set; } = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Указание, является ли тип элемента простым.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsSimple => Types.Count == 1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Конструктор корневого элемента.
|
||||||
|
/// </summary>
|
||||||
|
public SchemaNode()
|
||||||
{
|
{
|
||||||
/// <summary>
|
this.Root = true;
|
||||||
/// Отображаемое имя элемента.
|
}
|
||||||
/// </summary>
|
|
||||||
public string Name = "";
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Путь к элементу.
|
/// Конструктор элемента схемы данных.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Path = "";
|
/// <param name="parent">Родительский элемент.</param>
|
||||||
|
/// <param name="name">Имя элемента.</param>
|
||||||
|
public SchemaNode(SchemaNode parent, string name)
|
||||||
|
{
|
||||||
|
this.Parent = parent;
|
||||||
|
this.Name = name;
|
||||||
|
this.Path = parent.Path + "." + name;
|
||||||
|
this.Resolver = parent.Resolver;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Родительский элемент.
|
/// Добавление нового возможного значения.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public SchemaNode? Parent = null;
|
/// <param name="value">Добавляемое значение</param>
|
||||||
|
public void AddValue(JToken? value)
|
||||||
/// <summary>
|
{
|
||||||
/// Указание, является ли элемент корневым.
|
if (value is null || Resolver.TokenType(value) == JTokenType.Null)
|
||||||
/// </summary>
|
|
||||||
public bool Root = false;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Корневой элемент схемы данных.
|
|
||||||
/// </summary>
|
|
||||||
public SchemaNode RootNode => Root ? this : Parent!.RootNode;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Типы данных, использующиеся в элементе.
|
|
||||||
/// </summary>
|
|
||||||
public List<AbstractSchemaType> Types = [];
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Указание, может ли этот элемент отсутсвовать в структуре данных.
|
|
||||||
/// </summary>
|
|
||||||
public bool IsNullable = false;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Объект для определения типа поля в схеме данных.
|
|
||||||
/// </summary>
|
|
||||||
public TypeResolver Resolver = new();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Количество значений, добавленных в тип.
|
|
||||||
/// </summary>
|
|
||||||
public int Values { get; protected set; } = 0;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Указание, является ли тип элемента простым.
|
|
||||||
/// </summary>
|
|
||||||
public bool IsSimple => Types.Count == 1;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Конструктор корневого элемента.
|
|
||||||
/// </summary>
|
|
||||||
public SchemaNode()
|
|
||||||
{
|
{
|
||||||
this.Root = true;
|
IsNullable = true;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
Values++;
|
||||||
/// Конструктор элемента схемы данных.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="parent">Родительский элемент.</param>
|
|
||||||
/// <param name="name">Имя элемента.</param>
|
|
||||||
public SchemaNode(SchemaNode parent, string name)
|
|
||||||
{
|
|
||||||
this.Parent = parent;
|
|
||||||
this.Name = name;
|
|
||||||
this.Path = parent.Path + "." + name;
|
|
||||||
this.Resolver = parent.Resolver;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
foreach (AbstractSchemaType type in Types)
|
||||||
/// Добавление нового возможного значения.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">Добавляемое значение</param>
|
|
||||||
public void AddValue(JToken? value)
|
|
||||||
{
|
{
|
||||||
if (value is null || Resolver.TokenType(value) == JTokenType.Null)
|
if (type.IsCompatibleValue(value))
|
||||||
{
|
{
|
||||||
IsNullable = true;
|
type.AddValue(value);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Values++;
|
|
||||||
|
|
||||||
foreach (var type in Types)
|
|
||||||
{
|
|
||||||
if (type.IsCompatibleValue(value))
|
|
||||||
{
|
|
||||||
type.AddValue(value);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var newType = Resolver.Resolve(this, value);
|
|
||||||
|
|
||||||
if (newType.IsCompatibleValue(value))
|
|
||||||
{
|
|
||||||
Types.Add(newType);
|
|
||||||
newType.AddValue(value);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var fallbackType = Resolver.Resolve(this, value, true);
|
|
||||||
Types.Add(fallbackType);
|
|
||||||
fallbackType.AddValue(value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
AbstractSchemaType newType = Resolver.Resolve(this, value);
|
||||||
/// Получение строкового представоения типа элемента.
|
|
||||||
/// </summary>
|
if (newType.IsCompatibleValue(value))
|
||||||
/// <returns>Строковое представоение типа элемента</returns>
|
|
||||||
public string GetTypeName()
|
|
||||||
{
|
{
|
||||||
if (IsSimple)
|
Types.Add(newType);
|
||||||
{
|
newType.AddValue(value);
|
||||||
return Types[0].GetTypeName();
|
|
||||||
}
|
|
||||||
|
|
||||||
var typeNames = Types.Select(t => t.GetTypeName()).ToList();
|
|
||||||
|
|
||||||
return string.Join(" | ", typeNames);
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
/// <summary>
|
|
||||||
/// Полное строковое представление элемента.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Строковое представление элемента.</returns>
|
|
||||||
public override string ToString()
|
|
||||||
{
|
{
|
||||||
var result = new Dictionary<string, string>()
|
AbstractSchemaType fallbackType = Resolver.Resolve(this, value, true);
|
||||||
{
|
Types.Add(fallbackType);
|
||||||
["type"] = GetTypeName(),
|
fallbackType.AddValue(value);
|
||||||
["path"] = Path,
|
|
||||||
["is_null"] = IsNullable ? "true" : "false",
|
|
||||||
["values"] = Values.ToString()
|
|
||||||
};
|
|
||||||
|
|
||||||
if (IsSimple)
|
|
||||||
{
|
|
||||||
return FormatYaml(result, Types[0].GetYamlData());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Types.Count == 0)
|
|
||||||
{
|
|
||||||
return FormatYaml(result, new() { ["is_empty"] = "true" });
|
|
||||||
}
|
|
||||||
|
|
||||||
List<string> types = [];
|
|
||||||
|
|
||||||
foreach (var type in Types)
|
|
||||||
{
|
|
||||||
types.Add(Indent(type?.ToString() ?? ""));
|
|
||||||
}
|
|
||||||
|
|
||||||
return FormatYaml(result, new() { ["types"] = "- " + string.Join("\r\n- ", types) });
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Подготовка элемента к красивому представлению.
|
|
||||||
/// </summary>
|
|
||||||
public void Beautify()
|
|
||||||
{
|
|
||||||
foreach (var type in Types)
|
|
||||||
{
|
|
||||||
type.Beautify();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Отступ для строкового представления.
|
|
||||||
/// </summary>
|
|
||||||
public const string Tab = " ";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Предел количество отображаемых элементов.
|
|
||||||
/// </summary>
|
|
||||||
public const int ElementLimit = 20;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Увеличение отступа строки.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="text">Изменяемая строка.</param>
|
|
||||||
/// <returns>Строка с добавленными отступами.</returns>
|
|
||||||
public static string Indent(string text)
|
|
||||||
{
|
|
||||||
return text.Replace("\r\n", "\r\n" + Tab);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Форматирование данных в YAML.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="input">Словарь данных.</param>
|
|
||||||
/// <returns>Строка форматированных данных</returns>
|
|
||||||
public static string FormatYaml(params List<Dictionary<string, string>> input)
|
|
||||||
{
|
|
||||||
var result = "";
|
|
||||||
|
|
||||||
foreach (var kvp in MergeDictionaries(input))
|
|
||||||
{
|
|
||||||
var val = kvp.Value.Contains("\r\n") ? $"\r\n{Tab}{Indent(kvp.Value)}" : kvp.Value;
|
|
||||||
|
|
||||||
result += $"{kvp.Key}: {val}\r\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
return result.Trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Dictionary<string, string> MergeDictionaries(params List<Dictionary<string, string>> input)
|
|
||||||
{
|
|
||||||
var result = new Dictionary<string, string>();
|
|
||||||
|
|
||||||
foreach (var item in input)
|
|
||||||
{
|
|
||||||
foreach (var kvp in item)
|
|
||||||
{
|
|
||||||
if (result.ContainsKey(kvp.Key))
|
|
||||||
{
|
|
||||||
result[kvp.Key] = kvp.Value;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
result.Add(kvp.Key, kvp.Value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Получение строкового представоения типа элемента.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Строковое представоение типа элемента</returns>
|
||||||
|
public string GetTypeName()
|
||||||
|
{
|
||||||
|
if (IsSimple)
|
||||||
|
{
|
||||||
|
return Types[0].GetTypeName();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<string> typeNames = [.. Types.Select(t => t.GetTypeName())];
|
||||||
|
|
||||||
|
return string.Join(" | ", typeNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Полное строковое представление элемента.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Строковое представление элемента.</returns>
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
Dictionary<string, string> result = new()
|
||||||
|
{
|
||||||
|
["type"] = GetTypeName(),
|
||||||
|
["path"] = Path,
|
||||||
|
["is_null"] = IsNullable ? "true" : "false",
|
||||||
|
["values"] = Values.ToString()
|
||||||
|
};
|
||||||
|
|
||||||
|
if (IsSimple)
|
||||||
|
{
|
||||||
|
return FormatYaml(result, Types[0].GetYamlData());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Types.Count == 0)
|
||||||
|
{
|
||||||
|
return FormatYaml(result, new() { ["is_empty"] = "true" });
|
||||||
|
}
|
||||||
|
|
||||||
|
List<string> types = [];
|
||||||
|
|
||||||
|
foreach (AbstractSchemaType type in Types)
|
||||||
|
{
|
||||||
|
types.Add(Indent(type?.ToString() ?? ""));
|
||||||
|
}
|
||||||
|
|
||||||
|
return FormatYaml(result, new() { ["types"] = "- " + string.Join("\r\n- ", types) });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Подготовка элемента к красивому представлению.
|
||||||
|
/// </summary>
|
||||||
|
public void Beautify()
|
||||||
|
{
|
||||||
|
foreach (AbstractSchemaType type in Types)
|
||||||
|
{
|
||||||
|
type.Beautify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Отступ для строкового представления.
|
||||||
|
/// </summary>
|
||||||
|
public const string Tab = " ";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Предел количество отображаемых элементов.
|
||||||
|
/// </summary>
|
||||||
|
public const int ElementLimit = 20;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Увеличение отступа строки.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="text">Изменяемая строка.</param>
|
||||||
|
/// <returns>Строка с добавленными отступами.</returns>
|
||||||
|
public static string Indent(string text)
|
||||||
|
{
|
||||||
|
return text.Replace("\r\n", "\r\n" + Tab);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Форматирование данных в YAML.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input">Словарь данных.</param>
|
||||||
|
/// <returns>Строка форматированных данных</returns>
|
||||||
|
public static string FormatYaml(params List<Dictionary<string, string>> input)
|
||||||
|
{
|
||||||
|
string result = "";
|
||||||
|
|
||||||
|
foreach (KeyValuePair<string, string> kvp in MergeDictionaries(input))
|
||||||
|
{
|
||||||
|
string val = kvp.Value.Contains("\r\n") ? $"\r\n{Tab}{Indent(kvp.Value)}" : kvp.Value;
|
||||||
|
|
||||||
|
result += $"{kvp.Key}: {val}\r\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.Trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Dictionary<string, string> MergeDictionaries(params List<Dictionary<string, string>> input)
|
||||||
|
{
|
||||||
|
Dictionary<string, string> result = [];
|
||||||
|
|
||||||
|
foreach (Dictionary<string, string> item in input)
|
||||||
|
{
|
||||||
|
foreach (KeyValuePair<string, string> kvp in item)
|
||||||
|
{
|
||||||
|
if (result.ContainsKey(kvp.Key))
|
||||||
|
{
|
||||||
|
result[kvp.Key] = kvp.Value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result.Add(kvp.Key, kvp.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,125 +1,111 @@
|
|||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using Pf2eDataScrapper.SchemaBuilder.Types;
|
using Pf2eDataScrapper.SchemaBuilder.Types;
|
||||||
|
|
||||||
namespace Pf2eDataScrapper.SchemaBuilder
|
namespace Pf2eDataScrapper.SchemaBuilder;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Класс для определения типа поля в схеме данных.
|
||||||
|
/// </summary>
|
||||||
|
internal class TypeResolver
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Класс для определения типа поля в схеме данных.
|
/// Проверка соответствия пути.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal class TypeResolver
|
/// <param name="node">Элемент, путь которого проверяется.</param>
|
||||||
|
/// <param name="path">Проверяемый путь. Допускается использование `*` в местах, где допускается любое значение.</param>
|
||||||
|
/// <returns>`true`, если `node.Path` соответсвует указанному в `path` значению.</returns>
|
||||||
|
public bool CheckPath(SchemaNode node, string path)
|
||||||
{
|
{
|
||||||
/// <summary>
|
if (!path.Contains("*"))
|
||||||
/// Проверка соответствия пути.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="node">Элемент, путь которого проверяется.</param>
|
|
||||||
/// <param name="path">Проверяемый путь. Допускается использование `*` в местах, где допускается любое значение.</param>
|
|
||||||
/// <returns>`true`, если `node.Path` соответсвует указанному в `path` значению.</returns>
|
|
||||||
public bool CheckPath(SchemaNode node, string path)
|
|
||||||
{
|
{
|
||||||
if (!path.Contains("*"))
|
return path == node.Path;
|
||||||
{
|
|
||||||
return path == node.Path;
|
|
||||||
}
|
|
||||||
|
|
||||||
var pathParts = path.Split('.');
|
|
||||||
var nodeParts = node.Path.Split('.');
|
|
||||||
|
|
||||||
if (pathParts.Length > nodeParts.Length)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return !pathParts.Where((t, i) => t != "*" && t != nodeParts[i]).Any();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
string[] pathParts = path.Split('.');
|
||||||
/// Проверка соответствия пути.
|
string[] nodeParts = node.Path.Split('.');
|
||||||
/// </summary>
|
|
||||||
/// <param name="node">Элемент, путь которого проверяется.</param>
|
return pathParts.Length > nodeParts.Length ? false : !pathParts.Where((t, i) => t != "*" && t != nodeParts[i]).Any();
|
||||||
/// <param name="paths">Проверяемые пути. Допускается использование `*` в местах, где допускается любое значение.</param>
|
}
|
||||||
/// <returns>`true`, если `node.Path` соответсвует хотя бы одному указанному в `paths` значению.</returns>
|
|
||||||
public bool CheckPath(SchemaNode node, ICollection<string> paths)
|
/// <summary>
|
||||||
|
/// Проверка соответствия пути.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="node">Элемент, путь которого проверяется.</param>
|
||||||
|
/// <param name="paths">Проверяемые пути. Допускается использование `*` в местах, где допускается любое значение.</param>
|
||||||
|
/// <returns>`true`, если `node.Path` соответсвует хотя бы одному указанному в `paths` значению.</returns>
|
||||||
|
public bool CheckPath(SchemaNode node, ICollection<string> paths)
|
||||||
|
{
|
||||||
|
return paths.Any(path => CheckPath(node, path));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Получение типа значения.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">Значение.</param>
|
||||||
|
/// <returns>Типа значения.</returns>
|
||||||
|
public JTokenType TokenType(JToken? value)
|
||||||
|
{
|
||||||
|
return value is null || value.Type is JTokenType.Null or JTokenType.Undefined or JTokenType.None ? JTokenType.Null : value.Type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Частное определение типа поля.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="node">Поле схемы данных, для которого создаётся тип.</param>
|
||||||
|
/// <param name="value">Добавляемое значение.</param>
|
||||||
|
/// <returns>Новый тип для схемы данных. `null`, что бы использовать значение по умолчанию</returns>
|
||||||
|
protected virtual AbstractSchemaType? CustomResolve(SchemaNode node, JToken? value)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Определение типа поля.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="node">Поле схемы данных, для которого создаётся тип.</param>
|
||||||
|
/// <param name="value">Добавляемое значение.</param>
|
||||||
|
/// <param name="disableResolve">Добавляемое значение.</param>
|
||||||
|
/// <returns>Новый тип для схемы данных</returns>
|
||||||
|
/// <exception cref="ArgumentNullException">
|
||||||
|
/// Возникает, если у `value` значение идентично `null`
|
||||||
|
/// (такие значения должны обрабаотываться в `SchemaNode`).
|
||||||
|
/// </exception>
|
||||||
|
public AbstractSchemaType Resolve(SchemaNode node, JToken? value, bool disableResolve)
|
||||||
|
{
|
||||||
|
if (!disableResolve)
|
||||||
{
|
{
|
||||||
return paths.Any(path => CheckPath(node, path));
|
AbstractSchemaType? customType = this.CustomResolve(node, value);
|
||||||
|
|
||||||
|
if (customType is not null)
|
||||||
|
{
|
||||||
|
return customType;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
JTokenType type = TokenType(value);
|
||||||
/// Получение типа значения.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">Значение.</param>
|
|
||||||
/// <returns>Типа значения.</returns>
|
|
||||||
public JTokenType TokenType(JToken? value)
|
|
||||||
{
|
|
||||||
if (value is null || value.Type is JTokenType.Null or JTokenType.Undefined or JTokenType.None)
|
|
||||||
{
|
|
||||||
return JTokenType.Null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return value.Type;
|
return type == JTokenType.Null
|
||||||
}
|
? throw new ArgumentNullException(nameof(value))
|
||||||
|
: type switch
|
||||||
/// <summary>
|
|
||||||
/// Частное определение типа поля.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="node">Поле схемы данных, для которого создаётся тип.</param>
|
|
||||||
/// <param name="value">Добавляемое значение.</param>
|
|
||||||
/// <returns>Новый тип для схемы данных. `null`, что бы использовать значение по умолчанию</returns>
|
|
||||||
protected virtual AbstractSchemaType? CustomResolve(SchemaNode node, JToken? value)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Определение типа поля.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="node">Поле схемы данных, для которого создаётся тип.</param>
|
|
||||||
/// <param name="value">Добавляемое значение.</param>
|
|
||||||
/// <param name="disableResolve">Добавляемое значение.</param>
|
|
||||||
/// <returns>Новый тип для схемы данных</returns>
|
|
||||||
/// <exception cref="ArgumentNullException">
|
|
||||||
/// Возникает, если у `value` значение идентично `null`
|
|
||||||
/// (такие значения должны обрабаотываться в `SchemaNode`).
|
|
||||||
/// </exception>
|
|
||||||
public AbstractSchemaType Resolve(SchemaNode node, JToken? value, bool disableResolve)
|
|
||||||
{
|
|
||||||
if (!disableResolve)
|
|
||||||
{
|
|
||||||
var customType = this.CustomResolve(node, value);
|
|
||||||
|
|
||||||
if (customType is not null)
|
|
||||||
{
|
|
||||||
return customType;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var type = TokenType(value);
|
|
||||||
|
|
||||||
if (type == JTokenType.Null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
return type switch
|
|
||||||
{
|
{
|
||||||
JTokenType.Array => new ArraySchemaType(node),
|
JTokenType.Array => new ArraySchemaType(node),
|
||||||
JTokenType.Object => new ObjectSchemaType(node),
|
JTokenType.Object => new ObjectSchemaType(node),
|
||||||
_ => new StaticSchemaType(node, type)
|
_ => new StaticSchemaType(node, type)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Определение типа поля.
|
/// Определение типа поля.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="node">Поле схемы данных, для которого создаётся тип.</param>
|
/// <param name="node">Поле схемы данных, для которого создаётся тип.</param>
|
||||||
/// <param name="value">Добавляемое значение.</param>
|
/// <param name="value">Добавляемое значение.</param>
|
||||||
/// <returns>Новый тип для схемы данных</returns>
|
/// <returns>Новый тип для схемы данных</returns>
|
||||||
/// <exception cref="ArgumentNullException">
|
/// <exception cref="ArgumentNullException">
|
||||||
/// Возникает, если у `value` значение идентично `null`
|
/// Возникает, если у `value` значение идентично `null`
|
||||||
/// (такие значения должны обрабаотываться в `SchemaNode`).
|
/// (такие значения должны обрабаотываться в `SchemaNode`).
|
||||||
/// </exception>
|
/// </exception>
|
||||||
public AbstractSchemaType Resolve(SchemaNode node, JToken? value)
|
public AbstractSchemaType Resolve(SchemaNode node, JToken? value)
|
||||||
{
|
{
|
||||||
return this.Resolve(node, value, false);
|
return this.Resolve(node, value, false);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,65 +1,64 @@
|
|||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
namespace Pf2eDataScrapper.SchemaBuilder.Types
|
namespace Pf2eDataScrapper.SchemaBuilder.Types;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Абстрактный тип представоления данных в схеме.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parent">Элемент, к которому добавляется тип.</param>
|
||||||
|
internal abstract class AbstractSchemaType(SchemaNode parent)
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Абстрактный тип представоления данных в схеме.
|
/// Элемент схемы данных, к которому относится тип.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="parent">Элемент, к которому добавляется тип.</param>
|
public SchemaNode Parent = parent;
|
||||||
internal abstract class AbstractSchemaType(SchemaNode parent)
|
|
||||||
|
/// <summary>
|
||||||
|
/// Корневой элемент схемы данных.
|
||||||
|
/// </summary>
|
||||||
|
public SchemaNode Root => Parent.RootNode;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Объект для определения типа поля в схеме данных.
|
||||||
|
/// </summary>
|
||||||
|
public TypeResolver Resolver => Root.Resolver;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Проверка, что значение совместимо с текущим типом.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">Проверяемое значение.</param>
|
||||||
|
/// <returns>`true`, если значение совместимо с текущим типом.</returns>
|
||||||
|
public abstract bool IsCompatibleValue(JToken value);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Добавление нового значения.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">Новое значение.</param>
|
||||||
|
public abstract void AddValue(JToken value);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Подготовка типа к красивому представлению.
|
||||||
|
/// </summary>
|
||||||
|
public abstract void Beautify();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Получение строкового представоения типа элемента.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Строковое представоение типа элемента</returns>
|
||||||
|
public abstract string GetTypeName();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Получение данных для форматирования в YAML.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Словарь данных</returns>
|
||||||
|
public abstract Dictionary<string, string> GetYamlData();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Полное строковое представление элемента.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Строковое представление элемента.</returns>
|
||||||
|
public override string ToString()
|
||||||
{
|
{
|
||||||
/// <summary>
|
return SchemaNode.FormatYaml(GetYamlData());
|
||||||
/// Элемент схемы данных, к которому относится тип.
|
|
||||||
/// </summary>
|
|
||||||
public SchemaNode Parent = parent;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Корневой элемент схемы данных.
|
|
||||||
/// </summary>
|
|
||||||
public SchemaNode Root => Parent.RootNode;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Объект для определения типа поля в схеме данных.
|
|
||||||
/// </summary>
|
|
||||||
public TypeResolver Resolver => Root.Resolver;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Проверка, что значение совместимо с текущим типом.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">Проверяемое значение.</param>
|
|
||||||
/// <returns>`true`, если значение совместимо с текущим типом.</returns>
|
|
||||||
public abstract bool IsCompatibleValue(JToken value);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Добавление нового значения.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">Новое значение.</param>
|
|
||||||
public abstract void AddValue(JToken value);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Подготовка типа к красивому представлению.
|
|
||||||
/// </summary>
|
|
||||||
public abstract void Beautify();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Получение строкового представоения типа элемента.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Строковое представоение типа элемента</returns>
|
|
||||||
public abstract string GetTypeName();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Получение данных для форматирования в YAML.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Словарь данных</returns>
|
|
||||||
public abstract Dictionary<string, string> GetYamlData();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Полное строковое представление элемента.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Строковое представление элемента.</returns>
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return SchemaNode.FormatYaml(GetYamlData());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,89 +1,88 @@
|
|||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
namespace Pf2eDataScrapper.SchemaBuilder.Types
|
namespace Pf2eDataScrapper.SchemaBuilder.Types;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Тип представоления массива в схеме.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parent">Элемент, к которому добавляется тип.</param>
|
||||||
|
internal class ArraySchemaType(SchemaNode parent) : AbstractSchemaType(parent)
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Тип представоления массива в схеме.
|
/// Минимальное количество элементов в массиве.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="parent">Элемент, к которому добавляется тип.</param>
|
public int MinCount = int.MaxValue;
|
||||||
internal class ArraySchemaType(SchemaNode parent) : AbstractSchemaType(parent)
|
|
||||||
|
/// <summary>
|
||||||
|
/// Максимальное количество элементов в массиве.
|
||||||
|
/// </summary>
|
||||||
|
public int MaxCount = int.MinValue;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Схема элемента массива.
|
||||||
|
/// </summary>
|
||||||
|
protected SchemaNode ElementSchema = new(parent, "[]");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Проверка, что значение совместимо с текущим типом.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">Проверяемое значение.</param>
|
||||||
|
/// <returns>`true`, если значение совместимо с текущим типом.</returns>
|
||||||
|
public override bool IsCompatibleValue(JToken value)
|
||||||
{
|
{
|
||||||
/// <summary>
|
return Resolver.TokenType(value) == JTokenType.Array;
|
||||||
/// Минимальное количество элементов в массиве.
|
}
|
||||||
/// </summary>
|
|
||||||
public int MinCount = int.MaxValue;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Максимальное количество элементов в массиве.
|
/// Добавление нового значения.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int MaxCount = int.MinValue;
|
/// <param name="value">Новое значение.</param>
|
||||||
|
public override void AddValue(JToken value)
|
||||||
/// <summary>
|
{
|
||||||
/// Схема элемента массива.
|
if (value.Type != JTokenType.Array)
|
||||||
/// </summary>
|
|
||||||
protected SchemaNode ElementSchema = new(parent, "[]");
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Проверка, что значение совместимо с текущим типом.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">Проверяемое значение.</param>
|
|
||||||
/// <returns>`true`, если значение совместимо с текущим типом.</returns>
|
|
||||||
public override bool IsCompatibleValue(JToken value)
|
|
||||||
{
|
{
|
||||||
return Resolver.TokenType(value) == JTokenType.Array;
|
throw new Exception("Value is not an array, did you forget to call IsCompatibleValue?");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
JArray array = (JArray) value;
|
||||||
/// Добавление нового значения.
|
|
||||||
/// </summary>
|
MinCount = Math.Min(MinCount, array.Count);
|
||||||
/// <param name="value">Новое значение.</param>
|
MaxCount = Math.Max(MaxCount, array.Count);
|
||||||
public override void AddValue(JToken value)
|
|
||||||
|
foreach (JToken item in array)
|
||||||
{
|
{
|
||||||
if (value.Type != JTokenType.Array)
|
ElementSchema.AddValue(item);
|
||||||
{
|
|
||||||
throw new Exception("Value is not an array, did you forget to call IsCompatibleValue?");
|
|
||||||
}
|
|
||||||
|
|
||||||
var array = (JArray)value;
|
|
||||||
|
|
||||||
MinCount = Math.Min(MinCount, array.Count);
|
|
||||||
MaxCount = Math.Max(MaxCount, array.Count);
|
|
||||||
|
|
||||||
foreach (var item in array)
|
|
||||||
{
|
|
||||||
ElementSchema.AddValue(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Подготовка типа к красивому представлению.
|
|
||||||
/// </summary>
|
|
||||||
public override void Beautify()
|
|
||||||
{
|
|
||||||
ElementSchema.Beautify();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Получение строкового представоения типа элемента.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Строковое представоение типа элемента</returns>
|
|
||||||
public override string GetTypeName()
|
|
||||||
{
|
|
||||||
return $"array<{ElementSchema.GetTypeName()}>";
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Получение данных для форматирования в YAML.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Словарь данных</returns>
|
|
||||||
public override Dictionary<string, string> GetYamlData()
|
|
||||||
{
|
|
||||||
return new()
|
|
||||||
{
|
|
||||||
["min_count"] = MinCount.ToString(),
|
|
||||||
["max_count"] = MaxCount.ToString(),
|
|
||||||
["value_type"] = ElementSchema.ToString()
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Подготовка типа к красивому представлению.
|
||||||
|
/// </summary>
|
||||||
|
public override void Beautify()
|
||||||
|
{
|
||||||
|
ElementSchema.Beautify();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Получение строкового представоения типа элемента.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Строковое представоение типа элемента</returns>
|
||||||
|
public override string GetTypeName()
|
||||||
|
{
|
||||||
|
return $"array<{ElementSchema.GetTypeName()}>";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Получение данных для форматирования в YAML.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Словарь данных</returns>
|
||||||
|
public override Dictionary<string, string> GetYamlData()
|
||||||
|
{
|
||||||
|
return new()
|
||||||
|
{
|
||||||
|
["min_count"] = MinCount.ToString(),
|
||||||
|
["max_count"] = MaxCount.ToString(),
|
||||||
|
["value_type"] = ElementSchema.ToString()
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,77 +1,76 @@
|
|||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
namespace Pf2eDataScrapper.SchemaBuilder.Types
|
namespace Pf2eDataScrapper.SchemaBuilder.Types;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Тип представоления словаря в схеме.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parent">Элемент, к которому добавляется тип.</param>
|
||||||
|
internal class DictionarySchemaType(SchemaNode parent) : AbstractSchemaType(parent)
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Тип представоления словаря в схеме.
|
/// Схема ключей словаря.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="parent">Элемент, к которому добавляется тип.</param>
|
public SchemaNode KeyNode = new(parent, "[key]");
|
||||||
internal class DictionarySchemaType(SchemaNode parent) : AbstractSchemaType(parent)
|
|
||||||
|
/// <summary>
|
||||||
|
/// Схема значений словаря.
|
||||||
|
/// </summary>
|
||||||
|
public SchemaNode ItemNode = new(parent, "[value]");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Проверка, что значение совместимо с текущим типом.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">Проверяемое значение.</param>
|
||||||
|
/// <returns>`true`, если значение совместимо с текущим типом.</returns>
|
||||||
|
public override bool IsCompatibleValue(JToken value)
|
||||||
{
|
{
|
||||||
/// <summary>
|
return Resolver.TokenType(value) == JTokenType.Object;
|
||||||
/// Схема ключей словаря.
|
}
|
||||||
/// </summary>
|
|
||||||
public SchemaNode KeyNode = new(parent, "[key]");
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Схема значений словаря.
|
/// Добавление нового значения.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public SchemaNode ItemNode = new(parent, "[value]");
|
/// <param name="value">Новое значение.</param>
|
||||||
|
public override void AddValue(JToken value)
|
||||||
|
{
|
||||||
|
JObject obj = (JObject) value;
|
||||||
|
|
||||||
/// <summary>
|
foreach (KeyValuePair<string, JToken?> pair in obj)
|
||||||
/// Проверка, что значение совместимо с текущим типом.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">Проверяемое значение.</param>
|
|
||||||
/// <returns>`true`, если значение совместимо с текущим типом.</returns>
|
|
||||||
public override bool IsCompatibleValue(JToken value)
|
|
||||||
{
|
{
|
||||||
return Resolver.TokenType(value) == JTokenType.Object;
|
KeyNode.AddValue(pair.Key);
|
||||||
}
|
ItemNode.AddValue(pair.Value);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Добавление нового значения.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">Новое значение.</param>
|
|
||||||
public override void AddValue(JToken value)
|
|
||||||
{
|
|
||||||
var obj = (JObject)value;
|
|
||||||
|
|
||||||
foreach (var pair in obj)
|
|
||||||
{
|
|
||||||
KeyNode.AddValue(pair.Key);
|
|
||||||
ItemNode.AddValue(pair.Value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Подготовка типа к красивому представлению.
|
|
||||||
/// </summary>
|
|
||||||
public override void Beautify()
|
|
||||||
{
|
|
||||||
KeyNode.Beautify();
|
|
||||||
ItemNode.Beautify();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Получение строкового представоения типа элемента.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Строковое представоение типа элемента</returns>
|
|
||||||
public override string GetTypeName()
|
|
||||||
{
|
|
||||||
return $"dict<{KeyNode.GetTypeName()}, {ItemNode.GetTypeName()}>";
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Получение данных для форматирования в YAML.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Словарь данных</returns>
|
|
||||||
public override Dictionary<string, string> GetYamlData()
|
|
||||||
{
|
|
||||||
return new Dictionary<string, string>()
|
|
||||||
{
|
|
||||||
["_key"] = KeyNode.ToString(),
|
|
||||||
["_value"] = ItemNode.ToString()
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Подготовка типа к красивому представлению.
|
||||||
|
/// </summary>
|
||||||
|
public override void Beautify()
|
||||||
|
{
|
||||||
|
KeyNode.Beautify();
|
||||||
|
ItemNode.Beautify();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Получение строкового представоения типа элемента.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Строковое представоение типа элемента</returns>
|
||||||
|
public override string GetTypeName()
|
||||||
|
{
|
||||||
|
return $"dict<{KeyNode.GetTypeName()}, {ItemNode.GetTypeName()}>";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Получение данных для форматирования в YAML.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Словарь данных</returns>
|
||||||
|
public override Dictionary<string, string> GetYamlData()
|
||||||
|
{
|
||||||
|
return new Dictionary<string, string>()
|
||||||
|
{
|
||||||
|
["_key"] = KeyNode.ToString(),
|
||||||
|
["_value"] = ItemNode.ToString()
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,55 +1,54 @@
|
|||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
namespace Pf2eDataScrapper.SchemaBuilder.Types
|
namespace Pf2eDataScrapper.SchemaBuilder.Types;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Тип, указывающий, что данные игнорируются.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parent">Элемент, к которому добавляется тип.</param>
|
||||||
|
internal class IgnoreSchemaType(SchemaNode parent) : AbstractSchemaType(parent)
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Тип, указывающий, что данные игнорируются.
|
/// Проверка, что значение совместимо с текущим типом.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="parent">Элемент, к которому добавляется тип.</param>
|
/// <param name="value">Проверяемое значение.</param>
|
||||||
internal class IgnoreSchemaType(SchemaNode parent) : AbstractSchemaType(parent)
|
/// <returns>`true`, если значение совместимо с текущим типом.</returns>
|
||||||
|
public override bool IsCompatibleValue(JToken value)
|
||||||
{
|
{
|
||||||
/// <summary>
|
return true;
|
||||||
/// Проверка, что значение совместимо с текущим типом.
|
}
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">Проверяемое значение.</param>
|
|
||||||
/// <returns>`true`, если значение совместимо с текущим типом.</returns>
|
|
||||||
public override bool IsCompatibleValue(JToken value)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Добавление нового значения.
|
/// Добавление нового значения.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="value">Новое значение.</param>
|
/// <param name="value">Новое значение.</param>
|
||||||
public override void AddValue(JToken value)
|
public override void AddValue(JToken value)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Подготовка типа к красивому представлению.
|
/// Подготовка типа к красивому представлению.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public override void Beautify()
|
public override void Beautify()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Получение строкового представоения типа элемента.
|
/// Получение строкового представоения типа элемента.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>Строковое представоение типа элемента</returns>
|
/// <returns>Строковое представоение типа элемента</returns>
|
||||||
public override string GetTypeName()
|
public override string GetTypeName()
|
||||||
{
|
{
|
||||||
return "ignore";
|
return "ignore";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Получение данных для форматирования в YAML.
|
/// Получение данных для форматирования в YAML.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>Словарь данных</returns>
|
/// <returns>Словарь данных</returns>
|
||||||
public override Dictionary<string, string> GetYamlData()
|
public override Dictionary<string, string> GetYamlData()
|
||||||
{
|
{
|
||||||
return [];
|
return [];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,124 +1,110 @@
|
|||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
namespace Pf2eDataScrapper.SchemaBuilder.Types
|
namespace Pf2eDataScrapper.SchemaBuilder.Types;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Тип представоления объектов в схеме.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parent">Элемент, к которому добавляется тип.</param>
|
||||||
|
internal class ObjectSchemaType(SchemaNode parent) : AbstractSchemaType(parent)
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Тип представоления объектов в схеме.
|
/// Элемнты полей объекта.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="parent">Элемент, к которому добавляется тип.</param>
|
public List<SchemaNode> Fields = [];
|
||||||
internal class ObjectSchemaType(SchemaNode parent) : AbstractSchemaType(parent)
|
|
||||||
|
/// <summary>
|
||||||
|
/// Проверка, что значение совместимо с текущим типом.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">Проверяемое значение.</param>
|
||||||
|
/// <returns>`true`, если значение совместимо с текущим типом.</returns>
|
||||||
|
public override bool IsCompatibleValue(JToken value)
|
||||||
{
|
{
|
||||||
/// <summary>
|
return Resolver.TokenType(value) == JTokenType.Object;
|
||||||
/// Элемнты полей объекта.
|
}
|
||||||
/// </summary>
|
|
||||||
public List<SchemaNode> Fields = [];
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Проверка, что значение совместимо с текущим типом.
|
/// Добавление нового значения.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="value">Проверяемое значение.</param>
|
/// <param name="value">Новое значение.</param>
|
||||||
/// <returns>`true`, если значение совместимо с текущим типом.</returns>
|
public override void AddValue(JToken value)
|
||||||
public override bool IsCompatibleValue(JToken value)
|
{
|
||||||
|
JObject obj = (JObject) value;
|
||||||
|
|
||||||
|
foreach (SchemaNode field in Fields)
|
||||||
{
|
{
|
||||||
return Resolver.TokenType(value) == JTokenType.Object;
|
if (!obj.ContainsKey(field.Name))
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Добавление нового значения.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">Новое значение.</param>
|
|
||||||
public override void AddValue(JToken value)
|
|
||||||
{
|
|
||||||
var obj = (JObject)value;
|
|
||||||
|
|
||||||
foreach (var field in Fields)
|
|
||||||
{
|
{
|
||||||
if (!obj.ContainsKey(field.Name))
|
field.IsNullable = true;
|
||||||
{
|
|
||||||
field.IsNullable = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var pair in obj)
|
|
||||||
{
|
|
||||||
var field = Fields.FirstOrDefault(f => f.Name == pair.Key);
|
|
||||||
|
|
||||||
if (field is null)
|
|
||||||
{
|
|
||||||
field = new SchemaNode(Parent, pair.Key);
|
|
||||||
Fields.Add(field);
|
|
||||||
}
|
|
||||||
|
|
||||||
field.AddValue(pair.Value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
foreach (KeyValuePair<string, JToken?> pair in obj)
|
||||||
/// Подготовка типа к красивому представлению.
|
|
||||||
/// </summary>
|
|
||||||
public override void Beautify()
|
|
||||||
{
|
{
|
||||||
Fields.Sort((a, b) =>
|
SchemaNode? field = Fields.FirstOrDefault(f => f.Name == pair.Key);
|
||||||
|
|
||||||
|
if (field is null)
|
||||||
{
|
{
|
||||||
if (a.IsNullable && !b.IsNullable)
|
field = new SchemaNode(Parent, pair.Key);
|
||||||
{
|
Fields.Add(field);
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return string.Compare(a.Name, b.Name, StringComparison.Ordinal);
|
|
||||||
});
|
|
||||||
|
|
||||||
foreach (var field in Fields)
|
|
||||||
{
|
|
||||||
field.Beautify();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Получение строкового представоения типа элемента.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Строковое представоение типа элемента</returns>
|
|
||||||
public override string GetTypeName()
|
|
||||||
{
|
|
||||||
return "object";
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Получение данных для форматирования структуры в YAML.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Словарь данных</returns>
|
|
||||||
public Dictionary<string, string> GetSchemaYaml()
|
|
||||||
{
|
|
||||||
var result = new Dictionary<string, string>();
|
|
||||||
|
|
||||||
foreach (var field in Fields)
|
|
||||||
{
|
|
||||||
var s = field.Name;
|
|
||||||
if (field.IsNullable)
|
|
||||||
{
|
|
||||||
s = $"[{s}]";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
s = $"'{s}'";
|
|
||||||
}
|
|
||||||
result.Add(s, field.ToString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
field.AddValue(pair.Value);
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Получение данных для форматирования в YAML.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Словарь данных</returns>
|
|
||||||
public override Dictionary<string, string> GetYamlData()
|
|
||||||
{
|
|
||||||
return new()
|
|
||||||
{
|
|
||||||
["schema"] = SchemaNode.FormatYaml(GetSchemaYaml())
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Подготовка типа к красивому представлению.
|
||||||
|
/// </summary>
|
||||||
|
public override void Beautify()
|
||||||
|
{
|
||||||
|
Fields.Sort((a, b) =>
|
||||||
|
{
|
||||||
|
return a.IsNullable && !b.IsNullable ? 0 : string.Compare(a.Name, b.Name, StringComparison.Ordinal);
|
||||||
|
});
|
||||||
|
|
||||||
|
foreach (SchemaNode field in Fields)
|
||||||
|
{
|
||||||
|
field.Beautify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Получение строкового представоения типа элемента.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Строковое представоение типа элемента</returns>
|
||||||
|
public override string GetTypeName()
|
||||||
|
{
|
||||||
|
return "object";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Получение данных для форматирования структуры в YAML.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Словарь данных</returns>
|
||||||
|
public Dictionary<string, string> GetSchemaYaml()
|
||||||
|
{
|
||||||
|
Dictionary<string, string> result = new();
|
||||||
|
|
||||||
|
foreach (SchemaNode field in Fields)
|
||||||
|
{
|
||||||
|
string s = field.Name;
|
||||||
|
s = field.IsNullable ? $"[{s}]" : $"'{s}'";
|
||||||
|
result.Add(s, field.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Получение данных для форматирования в YAML.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Словарь данных</returns>
|
||||||
|
public override Dictionary<string, string> GetYamlData()
|
||||||
|
{
|
||||||
|
return new()
|
||||||
|
{
|
||||||
|
["schema"] = SchemaNode.FormatYaml(GetSchemaYaml())
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,102 +1,101 @@
|
|||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
namespace Pf2eDataScrapper.SchemaBuilder.Types
|
namespace Pf2eDataScrapper.SchemaBuilder.Types;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Тип представоления простых значений в схеме.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parent">Элемент, к которому добавляется тип.</param>
|
||||||
|
/// <param name="type">Тип значения.</param>
|
||||||
|
internal class StaticSchemaType(SchemaNode parent, JTokenType type) : AbstractSchemaType(parent)
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Тип представоления простых значений в схеме.
|
/// Тип значения.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="parent">Элемент, к которому добавляется тип.</param>
|
protected JTokenType Type = type;
|
||||||
/// <param name="type">Тип значения.</param>
|
|
||||||
internal class StaticSchemaType(SchemaNode parent, JTokenType type) : AbstractSchemaType(parent)
|
/// <summary>
|
||||||
|
/// Набор возможных значений и их количества.
|
||||||
|
/// </summary>
|
||||||
|
protected Dictionary<JToken, int> Values = [];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Проверка, что значение совместимо с текущим типом.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">Проверяемое значение.</param>
|
||||||
|
/// <returns>`true`, если значение совместимо с текущим типом.</returns>
|
||||||
|
public override bool IsCompatibleValue(JToken value)
|
||||||
{
|
{
|
||||||
/// <summary>
|
return value.Type == Type;
|
||||||
/// Тип значения.
|
}
|
||||||
/// </summary>
|
|
||||||
protected JTokenType Type = type;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Набор возможных значений и их количества.
|
/// Добавление нового значения.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected Dictionary<JToken, int> Values = [];
|
/// <param name="value">Новое значение.</param>
|
||||||
|
public override void AddValue(JToken value)
|
||||||
|
{
|
||||||
|
Values.TryAdd(value, 0);
|
||||||
|
|
||||||
/// <summary>
|
Values[value]++;
|
||||||
/// Проверка, что значение совместимо с текущим типом.
|
}
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">Проверяемое значение.</param>
|
/// <summary>
|
||||||
/// <returns>`true`, если значение совместимо с текущим типом.</returns>
|
/// Подготовка типа к красивому представлению.
|
||||||
public override bool IsCompatibleValue(JToken value)
|
/// </summary>
|
||||||
|
public override void Beautify()
|
||||||
|
{
|
||||||
|
if (Type == JTokenType.String)
|
||||||
{
|
{
|
||||||
return value.Type == Type;
|
Dictionary<JToken, int> newValues = new();
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
foreach (KeyValuePair<JToken, int> pair in Values)
|
||||||
/// Добавление нового значения.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">Новое значение.</param>
|
|
||||||
public override void AddValue(JToken value)
|
|
||||||
{
|
|
||||||
Values.TryAdd(value, 0);
|
|
||||||
|
|
||||||
Values[value]++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Подготовка типа к красивому представлению.
|
|
||||||
/// </summary>
|
|
||||||
public override void Beautify()
|
|
||||||
{
|
|
||||||
if (Type == JTokenType.String)
|
|
||||||
{
|
{
|
||||||
var newValues = new Dictionary<JToken, int>();
|
string newValue = "\"" + pair.Key.ToString().Trim().Replace("\"", "\\\"") + "\"";
|
||||||
|
newValues.TryAdd(newValue, 0);
|
||||||
foreach (var pair in Values)
|
newValues[newValue] += pair.Value;
|
||||||
{
|
|
||||||
var newValue = "\"" + pair.Key.ToString().Trim().Replace("\"", "\\\"") + "\"";
|
|
||||||
newValues.TryAdd(newValue, 0);
|
|
||||||
newValues[newValue] += pair.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
Values = newValues;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Values = Values.OrderByDescending(pair => pair.Value).ToDictionary(pair => pair.Key, pair => pair.Value);
|
Values = newValues;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
Values = Values.OrderByDescending(pair => pair.Value).ToDictionary(pair => pair.Key, pair => pair.Value);
|
||||||
/// Получение строкового представоения типа элемента.
|
}
|
||||||
/// </summary>
|
|
||||||
/// <returns>Строковое представоение типа элемента</returns>
|
/// <summary>
|
||||||
public override string GetTypeName()
|
/// Получение строкового представоения типа элемента.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Строковое представоение типа элемента</returns>
|
||||||
|
public override string GetTypeName()
|
||||||
|
{
|
||||||
|
return Type.ToString().ToLower();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Получение данных для форматирования в YAML.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Словарь данных</returns>
|
||||||
|
public override Dictionary<string, string> GetYamlData()
|
||||||
|
{
|
||||||
|
List<string> values = [];
|
||||||
|
int count = Values.Count;
|
||||||
|
|
||||||
|
string prefix = (count == 1) ? "" : "- ";
|
||||||
|
|
||||||
|
if (count is <= SchemaNode.ElementLimit and > 0 && (Type != JTokenType.String || !Values.Any(s => s.ToString().Contains('\n'))))
|
||||||
{
|
{
|
||||||
return Type.ToString().ToLower();
|
foreach (KeyValuePair<JToken, int> kvp in Values)
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Получение данных для форматирования в YAML.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Словарь данных</returns>
|
|
||||||
public override Dictionary<string, string> GetYamlData()
|
|
||||||
{
|
|
||||||
var values = new List<string>();
|
|
||||||
var count = Values.Count;
|
|
||||||
|
|
||||||
var prefix = (count == 1) ? "" : "- ";
|
|
||||||
|
|
||||||
if (count is <= SchemaNode.ElementLimit and > 0 && (Type != JTokenType.String || !Values.Any(s => s.ToString().Contains('\n'))))
|
|
||||||
{
|
{
|
||||||
foreach (var kvp in Values)
|
values.Add($"{prefix}{kvp.Key} (x{kvp.Value})");
|
||||||
{
|
|
||||||
values.Add($"{prefix}{kvp.Key} (x{kvp.Value})");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new()
|
|
||||||
{
|
|
||||||
["different_values"] = count.ToString() +
|
|
||||||
((count == Root.Values) ? " (all different)" : "") +
|
|
||||||
((count == 1) ? " (all same)" : ""),
|
|
||||||
["variants"] = (values.Count > 0) ? string.Join("\r\n", values) : "[omitted]"
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return new()
|
||||||
|
{
|
||||||
|
["different_values"] = count.ToString() +
|
||||||
|
((count == Root.Values) ? " (all different)" : "") +
|
||||||
|
((count == 1) ? " (all same)" : ""),
|
||||||
|
["variants"] = (values.Count > 0) ? string.Join("\r\n", values) : "[omitted]"
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user