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

This commit is contained in:
2025-10-02 13:29:30 +04:00
parent d23d81fb53
commit 2fa94851cb
3176 changed files with 27146 additions and 30584 deletions
+155
View File
@@ -0,0 +1,155 @@
using Pf2e.Database.Model.Base;
namespace Pf2e.Database
{
/// <summary>
/// Набор классов модели ORM EntityFramework.
/// </summary>
internal static class NamespaceDoc { }
}
namespace Pf2e.Database.Model
{
/// <summary>
/// Модель классов ORM EntityFramework.
/// </summary>
internal static class NamespaceDoc { }
}
namespace Pf2e.Database.Model.Base
{
/// <summary>
/// Описание корневой сущности модели: класса <see cref="Entity"/> и его прямых связей.
/// </summary>
internal static class NamespaceDoc { }
}
namespace Pf2e.Database.Model.Base.Keywords
{
/// <summary>
/// Классы, связанные с ключевыми словами, которые могут использоваться в разных типах сущностей.
/// </summary>
internal static class NamespaceDoc { }
}
namespace Pf2e.Database.Model.Base.Source
{
/// <summary>
/// Классы, связанные с источниками, которые могут использоваться в разных типах сущностей.
/// </summary>
internal static class NamespaceDoc { }
}
namespace Pf2e.Database.Model.Base.Targets
{
/// <summary>
/// Классы, связанные с целями внешних ссылок, которые могут использоваться в разных типах сущностей.
/// </summary>
internal static class NamespaceDoc { }
}
namespace Pf2e.Database.Model.Common
{
/// <summary>
/// Общее классы на которые могут ссылаться разные типа.
/// </summary>
internal static class NamespaceDoc { }
}
namespace Pf2e.Database.Model.Common.I18n
{
/// <summary>
/// Классы, связанные с интернационализацией (i18n) модели.
/// </summary>
internal static class NamespaceDoc { }
}
namespace Pf2e.Database.Model.Enum
{
/// <summary>
/// Описание всех перечислений, использующихся в модели.
/// </summary>
internal static class NamespaceDoc { }
}
namespace Pf2e.Database.Model.Structure
{
/// <summary>
/// Основные типы, которые связаны 1-к-1 с <see cref="Entity"/>.
/// Для указания этой связи используется интерфейс <see cref="IEntity"/>.
/// </summary>
internal static class NamespaceDoc { }
}
namespace Pf2e.Database.Migrations
{
/// <summary>
/// Миграции БД, генерируются в автомотическом режиме.
/// </summary>
/// <exclude/>
internal static class NamespaceDoc { }
}
namespace Pf2e.Database.Model.Structure.AncestryRelation
{
/// <summary>
/// Классы, описывающие связи класса <see cref="Ancestry" />.
/// </summary>
internal static class NamespaceDoc { }
}
namespace Pf2e.Database.Model.Structure.ArchetypeRelation
{
/// <summary>
/// Классы, описывающие связи класса <see cref="Archetype" />.
/// </summary>
internal static class NamespaceDoc { }
}
namespace Pf2e.Database.Model.Structure.BackgroundRelation
{
/// <summary>
/// Классы, описывающие связи класса <see cref="Background" />.
/// </summary>
internal static class NamespaceDoc { }
}
namespace Pf2e.Database.Model.Structure.ClassRelation
{
/// <summary>
/// Классы, описывающие связи класса <see cref="Class" />.
/// </summary>
internal static class NamespaceDoc { }
}
namespace Pf2e.Database.Model.Structure.DeityRelation
{
/// <summary>
/// Классы, описывающие связи класса <see cref="Deity" />.
/// </summary>
internal static class NamespaceDoc { }
}
namespace Pf2e.Database.Model.Structure.FeatRelation
{
/// <summary>
/// Классы, описывающие связи класса <see cref="Feat" />.
/// </summary>
internal static class NamespaceDoc { }
}
namespace Pf2e.Database.Model.Structure.RitualRelation
{
/// <summary>
/// Классы, описывающие связи класса <see cref="Ritual" />.
/// </summary>
internal static class NamespaceDoc { }
}
namespace Pf2e.Database.Model.Structure.SpellRelation
{
/// <summary>
/// Классы, описывающие связи класса <see cref="Spell" />.
/// </summary>
internal static class NamespaceDoc { }
}
+97
View File
@@ -0,0 +1,97 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
namespace Pf2e.Database;
/// <summary>
/// Класс, обеспецивающий общий способ для отчистки данных таблиц.
///
/// Источник: https://www.codeproject.com/Articles/5339402/Delete-All-Rows-in-Entity-Framework-Core-6
/// </summary>
public static class EfCleanHelper
{
/// <summary>
/// Определение полного названия таблицы по служебному классу.
/// </summary>
/// <param name="entityType">Служебный класс, определяющий таблицу базы данных.</param>
/// <param name="defaultSchemaName">Схема таблицы по умолчанию (<c>"public"</c> для PostgresSQL)</param>
/// <returns></returns>
private static string GetName(IEntityType entityType, string defaultSchemaName = "public")
{
object? schema = entityType.FindAnnotation("Relational:Schema")?.Value;
string tableName =
entityType.GetAnnotation("Relational:TableName").Value?.ToString()
?? throw new($"Не могу определить название таблицы для типа {entityType}");
string schemaName = schema?.ToString() ?? defaultSchemaName;
string name = $"\"{schemaName}\".\"{tableName}\"";
return name;
}
/// <summary>
/// Определение полного названия таблицы по типу сущности.
/// </summary>
/// <typeparam name="T">Тип искомой сущности.</typeparam>
/// <param name="dbContext">Контекст, в котором производится поиск таблицы.</param>
/// <returns>Строковое представление названия таблицы.</returns>
public static string TableName<T>(DbContext dbContext)
where T : class
{
IEntityType entityType =
dbContext.Model.FindEntityType(typeof(T))
?? throw new($"Не могу определить название таблицы для класса {typeof(T)}");
return GetName(entityType);
}
/// <summary>
/// Определение полного названия таблицы по типу сущности.
/// </summary>
/// <typeparam name="T">Тип искомой сущности.</typeparam>
/// <param name="dbSet">CLR-представление таблицы, название которой требуется определить.</param>
/// <returns>Строковое представление названия таблицы.</returns>
public static string TableName<T>(DbSet<T> dbSet)
where T : class
{
IEntityType entityType = dbSet.EntityType;
return GetName(entityType);
}
/// <summary>
/// Отчистка содержимого таблицы.
/// </summary>
/// <typeparam name="T">Тип сущности, таблицу которой необходимо отчистить.</typeparam>
/// <param name="dbSet">CLR-представление таблицы, которую требуется отчистить.</param>
/// <returns>Строковое представление выполненной SQL-команды.</returns>
public static string Truncate<T>(this DbSet<T> dbSet)
where T : class
{
string cmd = $"TRUNCATE TABLE {TableName(dbSet)} RESTART IDENTITY CASCADE;";
DbContext context = dbSet.GetService<ICurrentDbContext>().Context;
context.Database.ExecuteSqlRaw(cmd);
return cmd;
}
/// <summary>
/// Получение SQL-команды отчистки содержимого всех таблиц базы данных.
/// </summary>
/// <param name="context">Контекст базы данных, в которой необходимо отчистить все строки.</param>
/// <returns>Строковое представление выполненных SQL-команды.</returns>
public static string TruncateAll(this DbContext context)
{
IEnumerable<IEntityType> tables = context.Model.GetEntityTypes();
List<string> cmdList = [];
foreach (IEntityType entityType in tables)
{
string cmd = $"TRUNCATE TABLE {GetName(entityType)} RESTART IDENTITY CASCADE;";
context.Database.ExecuteSqlRaw(cmd);
cmdList.Add(cmd);
}
return string.Join('\n', cmdList);
}
}
+56
View File
@@ -0,0 +1,56 @@
using System.ComponentModel;
using System.Reflection;
using Pf2e.Database.Model;
using Pf2e.Database.Model.Base;
using Pf2e.Database.Model.Enum;
namespace Pf2e.Database;
/// <summary>
/// Аттрибут, указывающий тип сущности.
/// </summary>
/// <param name="type">Тип сущности.</param>
[AttributeUsage(AttributeTargets.Class)]
public class EntityOfTypeAttribute(EntityType type) : Attribute
{
/// <summary>
/// Тип сущности.
/// </summary>
public EntityType Type { get; } = type;
/// <summary>
/// Метод для получения программного типа сущности по её типу.
/// </summary>
/// <param name="entity">Тип сущности.</param>
/// <returns>Программный тип сущности.</returns>
/// <exception cref="Exception">Если есть ошибка в структурах.</exception>
/// <exception cref="InvalidEnumArgumentException">Тип сущности.</exception>
public static Type Resolve(Entity entity)
{
foreach (Type type in Assembly.GetExecutingAssembly().GetTypes())
{
if (type.GetCustomAttributes(typeof(EntityOfTypeAttribute), true).Length <= 0)
{
continue;
}
if (!type.IsAssignableTo(typeof(IEntity)))
{
throw new(
$"'EntityOfTypeAttribute' attribute should only be applied to children of 'IEntity' class. '{type.FullName}' is not."
);
}
EntityOfTypeAttribute attribute = type.GetCustomAttribute<EntityOfTypeAttribute>()!;
if (attribute.Type == entity.Type)
{
return type;
}
}
throw new InvalidEnumArgumentException(
$"Can't resolve class associated with '{entity.Type}' EntityType."
);
}
}
@@ -0,0 +1,24 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using Pf2e.Database.Model;
using Pf2e.Database.Model.Enum;
#nullable disable
namespace Pf2e.Database.Migrations
{
[DbContext(typeof(Pf2eDbContext))]
[Migration("20250418062358_Function")]
partial class Function
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
}
}
}
@@ -0,0 +1,93 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Pf2e.Database.Migrations;
/// <inheritdoc />
public partial class Function : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql(
"""
CREATE OR REPLACE FUNCTION f_drop_tables()
RETURNS void
LANGUAGE plpgsql AS
$func$
BEGIN
EXECUTE
(SELECT 'DROP TABLE '
|| string_agg(format('%I.%I', schemaname, tablename), ', ')
|| ' CASCADE;'
FROM pg_tables
WHERE schemaname = 'public'
);
END
$func$;
"""
);
migrationBuilder.Sql(
"""
CREATE OR REPLACE FUNCTION f_permission_tables(_username text)
RETURNS void
LANGUAGE plpgsql AS
$func$
DECLARE
_tbl text;
_sch text;
BEGIN
FOR _sch, _tbl IN
SELECT schemaname, tablename
FROM pg_tables
WHERE schemaname = 'public'
LOOP
EXECUTE format('ALTER TABLE %I.%I OWNER TO %I', _sch, _tbl, _username);
END LOOP;
END
$func$;
"""
);
migrationBuilder.Sql(
"""
CREATE OR REPLACE FUNCTION locale_search(data jsonb, search_value text)
RETURNS boolean
LANGUAGE plpgsql AS
$func$
BEGIN
RETURN jsonb_path_exists(data, '$.** ? (@ == $val)', jsonb_build_object('val', search_value));
END;
$func$;
"""
);
migrationBuilder.Sql(
"""
CREATE OR REPLACE FUNCTION locale_search_array(data jsonb, search_values text[])
RETURNS boolean
LANGUAGE plpgsql AS
$func$
BEGIN
RETURN EXISTS (
SELECT 1
FROM unnest(search_values) AS val
WHERE jsonb_path_exists(data, '$.** ? (@ == $val)', jsonb_build_object('val', val))
);
END;
$func$;
"""
);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql("DROP FUNCTION f_truncate_tables;");
migrationBuilder.Sql("DROP FUNCTION f_drop_tables;");
migrationBuilder.Sql("DROP FUNCTION f_permission_tables;");
migrationBuilder.Sql("DROP FUNCTION locale_search;");
migrationBuilder.Sql("DROP FUNCTION locale_search_array;");
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+134
View File
@@ -0,0 +1,134 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
using Pf2e.Database.Model.Base.Keywords;
using Pf2e.Database.Model.Base.Source;
using Pf2e.Database.Model.Base.Targets;
using Pf2e.Database.Model.Common.I18n;
using Pf2e.Database.Model.Enum;
using Pf2e.Database.Model.Structure;
namespace Pf2e.Database.Model.Base;
/// <summary>
/// Корневая сущность.
/// </summary>
public class Entity
{
/// <summary>
/// Идентификатор сущности.
/// </summary>
[Key]
[Column(Order = 1)]
public int Id { get; set; }
/// <summary>
/// Отображаемое имя сущности.
/// </summary>
[Column(Order = 2)]
[Comment("Отображаемое имя")]
public LocaleString Name { get; set; } = [];
/// <summary>
/// Тип сущности.
/// </summary>
[Column(Order = 3)]
[Comment("Тип сущности")]
public EntityType Type { get; set; }
/// <summary>
/// Часть имени в URL.
/// </summary>
[Column(Order = 4)]
[MaxLength(Pf2eDbContext.NameLength)]
[Comment("Часть имени в URL")]
public string Slug { get; set; } = null!;
/// <summary>
/// Изображение сущности.
/// </summary>
[Column(Order = 5)]
[StringLength(255)]
[Comment("Изображение объекта")]
public string Img { get; set; } = null!;
/// <summary>
/// Текстовое описание сущности.
/// </summary>
[Column(Order = 6)]
[Comment("Текстовое описание сущности")]
public string Description { get; set; } = null!;
/// <summary>
/// Редкость сущности.
/// </summary>
[Column(Order = 7)]
[Comment("Редкость сущности")]
public EntityRarity Rarity { get; set; } = EntityRarity.Common;
/// <summary>
/// Флаг, указывающий, что у объекта существует перевод.
/// </summary>
[Column(Order = 8)]
[Comment("Есть перевод?")]
public bool? IsTranslated { get; set; } = false;
/// <summary>
/// Флаг, указывающий, что объект был проверен и не требует автоматической перезаписи.
/// </summary>
[Column(Order = 9)]
[Comment("Проверено?")]
public bool? IsChecked { get; set; } = false;
/// <summary>
/// Флаг, указывающий, что сущность является устаревшей.
/// </summary>
[Column(Order = 10)]
[Comment("Устаревшая?")]
public bool? IsLegacy { get; set; } = false;
/// <summary>
/// Коллекция алиасов
/// </summary>
public ICollection<EntityAlias> Aliases { get; set; } = [];
/// <summary>
/// Коллекция признаков, связанных с сущностью.
/// </summary>
public ICollection<Trait> Traits { get; set; } = [];
/// <summary>
/// Коллекция ключевых слов, связанных с сущностью.
/// </summary>
public ICollection<Keyword> Keywords { get; set; } = [];
/// <summary>
/// Коллекция источников, связанных с сущностью.
/// </summary>
public ICollection<EntitySource> Sources { get; set; } = [];
/// <summary>
/// Коллекция внешних ссылок, связанных с сущностью.
/// </summary>
public ICollection<EntityExternal> Externals { get; set; } = [];
/// <summary>
/// Коллекция устаревших сущностей.
/// </summary>
public ICollection<Entity> LegacyEntities { get; set; } = [];
/// <summary>
/// Коллекция переходных объектов для устаревших сущностей.
/// </summary>
public ICollection<LegacyRemaster> LegacyTransferEntities { get; set; } = [];
/// <summary>
/// Коллекция обновлённых сущностей.
/// </summary>
public ICollection<Entity> RemasterEntities { get; set; } = [];
/// <summary>
/// Коллекция переходных объектов для обновлённых сущностей.
/// </summary>
public ICollection<LegacyRemaster> RemasterTransferEntities { get; set; } = [];
}
+40
View File
@@ -0,0 +1,40 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
namespace Pf2e.Database.Model.Base;
/// <summary>
/// Алиас сущности.
/// </summary>
public class EntityAlias
{
/// <summary>
/// Идентификатор источника.
/// </summary>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Column(Order = 1)]
[Comment("Идентификатор")]
public int Id { get; set; }
/// <summary>
/// Идентификатор связанной сущности.
/// </summary>
[Column(Order = 2)]
[Comment("Связанная сущность")]
public int EntityId { get; set; }
/// <summary>
/// Отображаемое имя источника.
/// </summary>
[Column(Order = 3)]
[MaxLength(Pf2eDbContext.NameLength)]
[Comment("Алиас")]
public string Name { get; set; } = null!;
/// <summary>
/// Связанная
/// </summary>
public Entity Entity { get; set; } = null!;
}
@@ -0,0 +1,45 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
using Pf2e.Database.Model.Common.I18n;
namespace Pf2e.Database.Model.Base.Keywords;
/// <summary>
/// Ключевое слово.
/// </summary>
public class Keyword
{
/// <summary>
/// Идентификатор.
/// </summary>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Column(Order = 1)]
public int Id { get; set; }
/// <summary>
/// Отображаемое имя ключевого слова.
/// </summary>
[Column(Order = 2)]
[Comment("Отображаемое имя")]
public LocaleString Name = [];
/// <summary>
/// Часть имени в URL.
/// </summary>
[Column(Order = 3)]
[MaxLength(Pf2eDbContext.NameLength)]
[Comment("Часть имени в URL")]
public string Slug { get; set; } = null!;
/// <summary>
/// Коллекция сущностей, которые используют это ключевое слово.
/// </summary>
public ICollection<Entity> Entities { get; set; } = [];
/// <summary>
/// Коллекция синонимов ключевого слова.
/// </summary>
public ICollection<KeywordAlias> Aliases { get; set; } = [];
}
@@ -0,0 +1,40 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
namespace Pf2e.Database.Model.Base.Keywords;
/// <summary>
/// Синоним ключевого слова.
/// </summary>
public class KeywordAlias
{
/// <summary>
/// Идентификатор.
/// </summary>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Column(Order = 1)]
[Comment("Идентификатор")]
public int Id { get; set; }
/// <summary>
/// Идентификатор связаннного ключевого слова.
/// </summary>
[Column(Order = 2)]
[Comment("Идентификатор ключевого слова")]
public int KeywordId { get; set; }
/// <summary>
/// Значение синонима ключевого слова.
/// </summary>
[Column(Order = 3)]
[MaxLength(Pf2eDbContext.NameLength)]
[Comment("Синоним")]
public string Value { get; set; } = null!;
/// <summary>
/// Ключевое слово, к которому относится синоним.
/// </summary>
public Keyword Keyword { get; set; } = null!;
}
@@ -0,0 +1,35 @@
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
namespace Pf2e.Database.Model.Base;
/// <summary>
/// Связь устаревших и обновлённых сущностей
/// </summary>
[PrimaryKey(nameof(LegacyId), nameof(RemasterId))]
public class LegacyRemaster
{
/// <summary>
/// Идентификатор устаревшей сущности.
/// </summary>
[Column(Order = 1)]
[Comment("Идентификатор сущности")]
public int LegacyId { get; set; }
/// <summary>
/// Идентификатор обновлённой сущности.
/// </summary>
[Column(Order = 2)]
[Comment("Идентификатор цели")]
public int RemasterId { get; set; }
/// <summary>
/// Устаревшая сущность.
/// </summary>
public Entity LegacyEntity { get; set; } = null!;
/// <summary>
/// Обновлённая сущность.
/// </summary>
public Entity RemasterEntity { get; set; } = null!;
}
@@ -0,0 +1,61 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
namespace Pf2e.Database.Model.Base.Source;
/// <summary>
/// Связь между сущностью и источником.
/// </summary>
public class EntitySource
{
/// <summary>
/// Идентификатор связи.
/// </summary>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Column(Order = 0)]
[Comment("Идентификатор")]
public int Id { get; set; }
/// <summary>
/// Идентификатор сущности.
/// </summary>
[Column(Order = 1)]
[Comment("Идентификатор сущности")]
public int EntityId { get; set; }
/// <summary>
/// Идентификатор источника.
/// </summary>
[Column(Order = 2)]
[Comment("Идентификатор источника")]
public int SourceId { get; set; }
/// <summary>
/// Дополнительная информация о источнике, например, номер страницы, номер тома и т.д.
/// </summary>
[Column(Order = 3)]
[MaxLength(255)]
[Comment("Номер страницы, эррата и т.д.")]
public string Suffix { get; set; } = null!;
/// <summary>
/// Флаг, указывающий, что объект является частью ремастера.
/// </summary>
[Column(Order = 4)]
[Comment("Флаг, указывающий, что объект является частью ремастера")]
public bool IsRemaster { get; set; }
/// <summary>
/// Сущность, к которой относится источник.
/// </summary>
[JsonIgnore]
public Entity Entity { get; set; } = null!;
/// <summary>
/// Источник, к которому относится сущность.
/// </summary>
public Source Source { get; set; } = null!;
}
+50
View File
@@ -0,0 +1,50 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
using Pf2e.Database.Model.Enum;
namespace Pf2e.Database.Model.Base.Source;
/// <summary>
/// Источник сущности.
/// </summary>
public class Source
{
/// <summary>
/// Идентификатор источника.
/// </summary>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Column(Order = 1)]
[Comment("Идентификатор")]
public int Id { get; set; }
/// <summary>
/// Отображаемое имя источника.
/// </summary>
[Column(Order = 2)]
[MaxLength(Pf2eDbContext.NameLength)]
[Comment("Отображаемое имя")]
public string Name { get; set; } = null!;
/// <summary>
/// Категория источника.
/// </summary>
[Column(Order = 3)]
[Comment("Категория источника")]
public SourceCategory Category { get; set; }
/// <summary>
/// Дата выхода источника.
/// </summary>
[Column(Order = 4)]
[Comment("Дата выхода источника")]
public DateOnly? ReleaseDate { get; set; }
/// <summary>
/// Связанные сущности.
/// </summary>
[JsonIgnore]
public ICollection<EntitySource> EntitySources { get; set; } = [];
}
@@ -0,0 +1,61 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
namespace Pf2e.Database.Model.Base.Targets;
/// <summary>
/// Внешняя ссылка сущности.
/// </summary>
[PrimaryKey(nameof(EntityId), nameof(TargetId))]
public class EntityExternal
{
/// <summary>
/// Идентификатор сущности.
/// </summary>
[Column(Order = 1)]
[Comment("Идентификатор сущности")]
public int EntityId { get; set; }
/// <summary>
/// Идентификатор цели.
/// </summary>
[Column(Order = 2)]
[Comment("Идентификатор цели")]
public int TargetId { get; set; }
/// <summary>
/// Идентификатор элемента по ссылке.
/// </summary>
[Column(Order = 3)]
[MaxLength(255)]
[Comment("Идентификатор элемента в ссылке")]
public string? ExternalId { get; set; } = null;
/// <summary>
/// Конечная часть URL внешней ссылки.
/// Для источников Foundry - полный UUID объекта.
/// </summary>
[Column(Order = 4)]
[MaxLength(255)]
[Comment("Часть URL до объекта. Для источников Foundry - полный UUID объекта")]
public string Suffix { get; set; } = "";
/// <summary>
/// Контрольная сумма объекта.
/// </summary>
[Column(Order = 5)]
[MaxLength(32)]
[Comment("Контрольная сумма объекта")]
public string Md5 { get; set; } = null!;
/// <summary>
/// Сущность, которая ссылается на внешнюю ссылку.
/// </summary>
public Entity Entity { get; set; } = null!;
/// <summary>
/// Цель внешней ссылки.
/// </summary>
public ExternalTarget Target { get; set; } = null!;
}
@@ -0,0 +1,51 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
namespace Pf2e.Database.Model.Base.Targets;
/// <summary>
/// Цель внешней ссылки.
/// </summary>
public class ExternalTarget
{
/// <summary>
/// Идентификатор цели.
/// </summary>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Column(Order = 1)]
[Comment("Идентификатор")]
public int Id { get; set; }
/// <summary>
/// Название цели.
/// </summary>
[Column(Order = 2)]
[StringLength(Pf2eDbContext.NameLength)]
[Comment("Отображаемое имя")]
public string Name { get; set; } = null!;
/// <summary>
/// Иконка цели.
/// </summary>
[Column(Order = 3)]
[StringLength(255)]
[Comment("Иконка цели")]
public string Icon { get; set; } = null!;
/// <summary>
/// Префикс для URL-адреса внешней ссылки.
/// </summary>
[Column(Order = 3)]
[StringLength(Pf2eDbContext.NameLength)]
[Comment("Префикс для URL-адреса внешней ссылки")]
public string Prefix { get; set; } = null!;
/// <summary>
/// Коллекция сущностей, которые ссылаются на эту цель.
/// </summary>
[JsonIgnore]
public ICollection<EntityExternal> EntityExternals { get; set; } = [];
}
+41
View File
@@ -0,0 +1,41 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
namespace Pf2e.Database.Model.Common;
/// <summary>
/// Частота использования.
/// </summary>
public class Frequency
{
/// <summary>
/// Идентификатор.
/// </summary>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Column(Order = 1)]
[Comment("Идентификатор")]
public int Id { get; set; }
/// <summary>
/// Максимальная продолжительсность.
/// </summary>
[Column(Order = 2)]
[Comment("Максимальная продолжительсность")]
public int Max { get; set; }
/// <summary>
/// Частота использования.
/// </summary>
[Column(Order = 3)]
[Comment("Частота использования")]
public int Per { get; set; }
/// <summary>
/// Значение использования.
/// </summary>
[Column(Order = 4)]
[Comment("Значение использования")]
public int? Value { get; set; }
}
@@ -0,0 +1,66 @@
using System.ComponentModel;
using System.Reflection;
namespace Pf2e.Database.Model.Common.I18n;
/// <summary>
/// Расширения для работы с локализацией.
/// </summary>
public static class LocaleExtension
{
/// <summary>
/// Получение строкового представления типа локализации.
/// </summary>
/// <param name="localeType">Тип локализации.</param>
/// <returns>Строковое представление типа локализации.</returns>
public static string GetTypeString(this LocaleType localeType)
{
FieldInfo fi = localeType.GetType().GetField(localeType.ToString())!;
DescriptionAttribute[] attributes = (DescriptionAttribute[])
fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
return (attributes.Length != 0 ? attributes.FirstOrDefault()?.Description : null)
?? localeType.ToString();
}
/// <summary>
/// Получение типа локализации по строковому представлению типа локализации.
/// </summary>
/// <param name="localeTypeString">Строковое представление типа локализации.</param>
/// <returns>Тип локализации.</returns>
/// <exception cref="InvalidEnumArgumentException">Исключение, если строковое представление не соответсвует ни одному типу локализации.</exception>
public static LocaleType GetTypeByString(this string localeTypeString)
{
foreach (FieldInfo field in typeof(LocaleType).GetFields())
{
if (
Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute))
is not DescriptionAttribute descriptionAttribute
)
{
continue;
}
if (descriptionAttribute.Description == localeTypeString)
{
return (LocaleType)field.GetValue(null)!;
}
}
throw new InvalidEnumArgumentException();
}
/*
public static IQueryable<T> WhereLocale(
this IQueryable<T> query, Expression<Func<T, LocaleString>> expression, string searchTerm) where T : class
{
return query.Where(e => DbContext.LocaleSearch(e.Name.ToJson(), searchTerm));
}
public static IQueryable<Entity> WhereLocale(
this IQueryable<Entity> query, ICollection<string> searchTerms)
{
return query.Where(e => EF.Functions.LocaleSearchArray(e.Name.ToJson(), searchTerms));
}*/
}
@@ -0,0 +1,26 @@
namespace Pf2e.Database.Model.Common.I18n;
/// <summary>
/// Элемент локализации.
/// </summary>
/// <param name="type">Тип локализации.</param>
/// <param name="value">Значение локализации.</param>
public class LocaleItem(LocaleType type, string value)
{
/// <summary>
/// Тип локализации.
/// </summary>
public LocaleType Locale { get; set; } = type;
/// <summary>
/// Значение локализации.
/// </summary>
public string Value { get; set; } = value;
/// <summary>
/// Конструктор класса <see cref="LocaleItem"/> по паре ключ-значение.
/// </summary>
/// <param name="item">Пара ключ-значение по строковому представлению типа.</param>
public LocaleItem(KeyValuePair<string, string> item)
: this(item.Key.GetTypeByString(), item.Value) { }
}
@@ -0,0 +1,246 @@
using System.Collections;
using Newtonsoft.Json.Linq;
namespace Pf2e.Database.Model.Common.I18n;
/// <summary>
/// Локализованная строка.
/// </summary>
public class LocaleString : ICollection<LocaleItem>
{
/// <summary>
/// Список локализаций строк.
/// </summary>
private readonly List<LocaleItem> _items = [];
/// <summary>
/// Проверяет наличие хотя бы одной локализации.
/// </summary>
private void AssureDefault()
{
if (_items.Count == 0)
{
_items.Add(new(LocaleType.English, string.Empty));
}
}
/// <summary>
/// Количество локализаций.
/// </summary>
public int Count => _items.Count;
/// <summary>
/// Коллекция реализованных типов локализаций.
/// </summary>
public ICollection<LocaleType> Types => _items.Select(c => c.Locale).ToList();
/// <inheritdoc/>
public bool IsReadOnly => false;
/// <summary>
/// Конструктор класса <see cref="LocaleString"/>.
/// </summary>
public LocaleString()
{
AssureDefault();
}
/// <summary>
/// Получение типа локализации по строковому представлению.
/// </summary>
/// <param name="localeString">Строковое представление.</param>
/// <returns>Тип локализации.</returns>
private LocaleType _E(string localeString) => localeString.GetTypeByString();
/// <summary>
/// Получение типа локализации с учётом её наличия в коллекции.
/// </summary>
/// <param name="locale">Искомый тип локализации.</param>
/// <returns>Реальный тип локализации.</returns>
private LocaleType RealLocale(LocaleType locale) =>
Contains(locale) ? locale : LocaleType.English;
/// <summary>
/// Получение типа локализации по строковому представлению с учётом её наличия в коллекции.
/// </summary>
/// <param name="localeString">Строковое представление искомого типа локализации.</param>
/// <returns>Реальный тип локализации.</returns>
private LocaleType RealLocale(string localeString) => RealLocale(_E(localeString));
/// <summary>
/// Получение объекта локализованной строки по типу локализации.
/// </summary>
/// <param name="locale">Искомый тип локализации.</param>
/// <returns>Объект локализованной строки.</returns>
public LocaleItem GetItem(LocaleType locale) => this.First(c => c.Locale == RealLocale(locale));
/// <summary>
/// Получение объекта локализованной строки по строковому представлению типа локализации.
/// </summary>
/// <param name="localeString">Строковое представление искомого типа локализации.</param>
/// <returns>Объект локализованной строки.</returns>
public LocaleItem GetItem(string localeString) =>
this.First(c => c.Locale == RealLocale(localeString));
/// <summary>
/// Получение локализованной строки по типу локализации.
/// </summary>
/// <param name="locale">Искомый тип локализации.</param>
/// <returns>Локализованная строка.</returns>
public string this[LocaleType locale]
{
get => GetItem(locale).Value;
set
{
if (Types.Contains(locale))
{
GetItem(locale).Value = value;
}
else
{
_items.Add(new(locale, value));
}
}
}
/// <summary>
/// Получение локализованной строки по типу локализации.
/// </summary>
/// <param name="localeString">Строковое представление искомого типа локализации.</param>
/// <returns>Локализованная строка.</returns>
public string this[string localeString]
{
get => this[_E(localeString)];
set => this[_E(localeString)] = value;
}
/// <summary>
/// Добавление объекта локализованной строки в коллекцию.
/// </summary>
/// <param name="item">Объект локализированной строки.</param>
public void Add(LocaleItem item)
{
this[item.Locale] = item.Value;
}
/// <summary>
/// Добавление локализованной строки в коллекцию.
/// </summary>
/// <param name="locale">Тип локализации.</param>
/// <param name="value">Строка локазации.</param>
public void Add(LocaleType locale, string value)
{
this[locale] = value;
}
/// <summary>
/// Отчистка коллекции локализованных строк.
/// </summary>
public void Clear()
{
_items.Clear();
AssureDefault();
}
/// <summary>
/// Проверка наличия объекта локализованной строки в коллекции.
/// </summary>
/// <param name="item">Объект локализированной строки.</param>
/// <returns>`true`, если объект есть; `false`, если нет.</returns>
public bool Contains(LocaleItem item)
{
return _items.Contains(item);
}
/// <summary>
/// Проверка наличия локализованной строки в коллекции по типу локализации.
/// </summary>
/// <param name="locale">Тип локализации.</param>
/// <returns>`true`, если искомый тип локализации есть; `false`, если нет.</returns>
public bool Contains(LocaleType locale)
{
return Types.Contains(locale);
}
/// <summary>
/// Проверка наличия локализованной строки в коллекции по строковому представлению типа локализации.
/// </summary>
/// <param name="localeString">Строковое представление искомого типа локализации.</param>
/// <returns>`true`, если искомый тип локализации есть; `false`, если нет.</returns>
public bool Contains(string localeString)
{
return this.Any(item => item.Value == localeString);
}
/// <inheritdoc/>
public void CopyTo(LocaleItem[] array, int arrayIndex)
{
throw new NotImplementedException();
}
/// <inheritdoc/>
public bool Remove(LocaleItem item)
{
bool result = _items.Remove(item);
if (Count == 0)
{
AssureDefault();
return false;
}
return result;
}
/// <inheritdoc/>
public IEnumerator<LocaleItem> GetEnumerator()
{
return _items.GetEnumerator();
}
/// <inheritdoc/>
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
/// <inheritdoc/>
public override string ToString()
{
return this[LocaleType.English];
}
/// <summary>
/// Сериализация локализованной строки в JSON-формат.
/// </summary>
/// <returns>Строковое представление JSON-представления.</returns>
public string Serialize()
{
JObject json = [];
foreach (LocaleItem item in _items)
{
json.Add(item.Locale.GetTypeString(), item.Value);
}
return json.ToString();
}
/// <summary>
/// Десериализация локализованной строки из JSON-формата.
/// </summary>
/// <param name="input">Строковое представление JSON-представления.</param>
/// <returns>Объект локализованной строки.</returns>
public static LocaleString Deserialize(string input)
{
JObject json = JObject.Parse(input);
LocaleString result = [];
foreach (KeyValuePair<string, JToken?> item in json)
{
result[item.Key] = item.Value?.ToString() ?? "";
}
return result;
}
}
@@ -0,0 +1,33 @@
using System.ComponentModel;
namespace Pf2e.Database.Model.Common.I18n;
/// <summary>
/// Тип локализации.
/// </summary>
public enum LocaleType
{
/// <summary>
/// Английский язык (основной).
/// </summary>
[Description("en")]
English,
/// <summary>
/// Русский язык.
/// </summary>
[Description("ru")]
Russian,
/// <summary>
/// Русский язык (вариант HobbyGames).
/// </summary>
[Description("ru.hobby")]
Hobby,
/// <summary>
/// Другой язык.
/// </summary>
[Description("other")]
Other,
}
+36
View File
@@ -0,0 +1,36 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
using Pf2e.Database.Model.Common.I18n;
using Pf2e.Database.Model.Enum;
namespace Pf2e.Database.Model.Common;
/// <summary>
/// Изучаемая способность.
/// </summary>
public class Training
{
/// <summary>
/// Идентификатор.
/// </summary>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Column(Order = 1)]
[Comment("Идентификатор")]
public int Id { get; set; }
/// <summary>
/// Название способности.
/// </summary>
[Column(Order = 2)]
[Comment("Название способности")]
public LocaleString Name { get; set; } = [];
/// <summary>
/// Тип способности.
/// </summary>
[Column(Order = 3)]
[Comment("Тип способности")]
public TrainingType Type { get; set; }
}
@@ -0,0 +1,75 @@
using NpgsqlTypes;
namespace Pf2e.Database.Model.Enum;
/// <summary>
/// Тип действия.
/// </summary>
public enum ActionCategory
{
/// <summary>
/// Неизвестный тип действия.
/// </summary>
[PgName("unknown")]
Unknown,
/// <summary>
/// Действие архетипа.
/// </summary>
[PgName("archetype")]
Archetype,
/// <summary>
/// Дествие навыка.
/// </summary>
[PgName("skill-action")]
SkillAction,
/// <summary>
/// Действие классовой способности.
/// </summary>
[PgName("class-feature")]
ClassFeature,
/// <summary>
/// Действие классовой черты.
/// </summary>
[PgName("class-feat")]
ClassFeat,
/// <summary>
/// Действие фамильяра.
/// </summary>
[PgName("familiar")]
Familiar,
/// <summary>
/// Действие тактики армии.
/// </summary>
[PgName("army_tactic")]
ArmyTactic,
/// <summary>
/// Действие войны армии.
/// </summary>
[PgName("army_war_action")]
ArmyWarAction,
/// <summary>
/// Активность королевства.
/// </summary>
[PgName("kingdom_activity")]
KingdomActivity,
/// <summary>
/// Действие черты королевства.
/// </summary>
[PgName("kingdom_feat")]
KingdomFeat,
/// <summary>
/// Действие особенности королевства.
/// </summary>
[PgName("kingdom_feature")]
KingdomFeature,
}
+135
View File
@@ -0,0 +1,135 @@
using NpgsqlTypes;
namespace Pf2e.Database.Model.Enum;
/// <summary>
/// Перечиленение количества действий.
/// </summary>
public enum ActionEnum
{
/// <summary>
/// Неизвестное количество действий.
/// </summary>
[PgName("unknown")]
Unknown,
/// <summary>
/// Одно действие.
/// </summary>
[PgName("1")]
SingleAction,
/// <summary>
/// Два действия.
/// </summary>
[PgName("2")]
TwoActions,
/// <summary>
/// Три действия.
/// </summary>
[PgName("3")]
ThreeActions,
/// <summary>
/// Реакция.
/// </summary>
[PgName("reaction")]
Reaction,
/// <summary>
/// Свободное действие.
/// </summary>
[PgName("free")]
FreeAction,
/// <summary>
/// Пассивное действие.
/// </summary>
[PgName("passive")]
Passive,
/// <summary>
/// Иное количество действий, не указанное в перечислении.
/// </summary>
[PgName("other")]
Other,
/// <summary>
/// От одного до трёх действий.
/// </summary>
[PgName("1-3")]
SingleActionToThreeActions,
/// <summary>
/// От одного до двух действий.
/// </summary>
[PgName("1-2")]
SingleActionToTwoActions,
/// <summary>
/// От двух до трёх действий.
/// </summary>
[PgName("2-3")]
TwoActionsToThreeActions,
/// <summary>
/// От двух действий до двух раундов.
/// </summary>
[PgName("2/2 rounds")]
TwoActionsToTwoRounds,
/// <summary>
/// Реакция или одно действие.
/// </summary>
[PgName("reaction/1")]
ReactionOrOneAction,
/// <summary>
/// Реакция или два действия.
/// </summary>
[PgName("reaction/2")]
ReactionOrTwoAction,
/// <summary>
/// Реакция или три действия.
/// </summary>
[PgName("reaction/3")]
ReactionOrThreeAction,
/// <summary>
/// Свободное действие или одно действие.
/// </summary>
[PgName("free/1")]
FreeActionOrOneAction,
/// <summary>
/// Одная минута.
/// </summary>
[PgName("1 minute")]
OneMinute,
/// <summary>
/// Пять минут.
/// </summary>
[PgName("5 minutes")]
FiveMinutes,
/// <summary>
/// Десять минут.
/// </summary>
[PgName("10 minutes")]
TenMinutes,
/// <summary>
/// Тридцать минут.
/// </summary>
[PgName("30 minutes")]
ThirtyMinutes,
/// <summary>
/// Один час.
/// </summary>
[PgName("1 hour")]
OneHour,
}
@@ -0,0 +1,51 @@
using NpgsqlTypes;
namespace Pf2e.Database.Model.Enum;
/// <summary>
/// Перечисление значений атрибутов.
/// </summary>
public enum AttributeValues
{
/// <summary>
/// Свободный атрибут.
/// </summary>
[PgName("free")]
Free,
/// <summary>
/// Сила.
/// </summary>
[PgName("str")]
Strength,
/// <summary>
/// Ловкость.
/// </summary>
[PgName("dex")]
Dexterity,
/// <summary>
/// Телосложение.
/// </summary>
[PgName("con")]
Constitution,
/// <summary>
/// Интеллект.
/// </summary>
[PgName("int")]
Intelligence,
/// <summary>
/// Мудрость.
/// </summary>
[PgName("wis")]
Wisdom,
/// <summary>
/// Харизма.
/// </summary>
[PgName("cha")]
Charisma,
}
@@ -0,0 +1,51 @@
using NpgsqlTypes;
namespace Pf2e.Database.Model.Enum;
/// <summary>
/// Тип элемента прогрессии класса.
/// </summary>
public enum ClassProgressionType
{
/// <summary>
/// Неизвестный тип прогрессии класса.
/// </summary>
[PgName("entity")]
Entity,
/// <summary>
/// Навык архетипа.
/// </summary>
[PgName("ancestry_feat")]
AncestryFeat,
/// <summary>
/// Классовая особенность.
/// </summary>
[PgName("class_feat")]
ClassFeat,
/// <summary>
/// Общий навык.
/// </summary>
[PgName("general_feat")]
GeneralFeat,
/// <summary>
/// Навык
/// </summary>
[PgName("skill_feat")]
SkillFeat,
/// <summary>
/// Увеличение навыка.
/// </summary>
[PgName("skill_increase")]
SkillIncrease,
/// <summary>
/// Увеличение атрибута.
/// </summary>
[PgName("attribute_increase")]
AttributeIncrease,
}
@@ -0,0 +1,45 @@
using NpgsqlTypes;
namespace Pf2e.Database.Model.Enum;
/// <summary>
/// Тип состояния
/// </summary>
public enum ConditionGroup
{
/// <summary>
/// Неизвестое состояние.
/// </summary>
[PgName("unknown")]
Unknown,
/// <summary>
/// Чувство.
/// </summary>
[PgName("senses")]
Senses,
/// <summary>
/// Способность.
/// </summary>
[PgName("abilities")]
Abilities,
/// <summary>
/// Смерть.
/// </summary>
[PgName("death")]
Death,
/// <summary>
/// Отношение. (???)
/// </summary>
[PgName("attitudes")]
Attitudes,
/// <summary>
/// Обнаружение.
/// </summary>
[PgName("detection")]
Detection,
}
+33
View File
@@ -0,0 +1,33 @@
using NpgsqlTypes;
namespace Pf2e.Database.Model.Enum;
/// <summary>
/// Тип божественной силы.
/// </summary>
public enum DeityFonts
{
/// <summary>
/// Без божественной силы.
/// </summary>
[PgName("none")]
None,
/// <summary>
/// Исцеление.
/// </summary>
[PgName("heal")]
Heal,
/// <summary>
/// Урон.
/// </summary>
[PgName("harm")]
Harm,
/// <summary>
/// Любой тип божественной силы.
/// </summary>
[PgName("any")]
Any,
}
@@ -0,0 +1,27 @@
using NpgsqlTypes;
namespace Pf2e.Database.Model.Enum;
/// <summary>
/// Тип освящения божества.
/// </summary>
public enum DeitySanctification
{
/// <summary>
/// Святость.
/// </summary>
[PgName("holy")]
Holy,
/// <summary>
/// Нечестивость.
/// </summary>
[PgName("unholy")]
Unholy,
/// <summary>
/// Любое.
/// </summary>
[PgName("any")]
Any,
}
+33
View File
@@ -0,0 +1,33 @@
using NpgsqlTypes;
namespace Pf2e.Database.Model.Enum;
/// <summary>
/// Тип божества.
/// </summary>
public enum DeityType
{
/// <summary>
/// Божество.
/// </summary>
[PgName("deity")]
Deity,
/// <summary>
/// Пантеон.
/// </summary>
[PgName("pantheon")]
Pantheon,
/// <summary>
/// Ковенант.
/// </summary>
[PgName("covenant")]
Covenant,
/// <summary>
/// Философия.
/// </summary>
[PgName("philosophy")]
Philosophy,
}
+51
View File
@@ -0,0 +1,51 @@
using NpgsqlTypes;
namespace Pf2e.Database.Model.Enum;
/// <summary>
/// Перечисление элементов.
/// </summary>
public enum ElementEnum
{
/// <summary>
/// Универсальный элемент.
/// </summary>
[PgName("universal")]
Universal,
/// <summary>
/// Воздух.
/// </summary>
[PgName("air")]
Air,
/// <summary>
/// Вода.
/// </summary>
[PgName("water")]
Water,
/// <summary>
/// Земля.
/// </summary>
[PgName("earth")]
Earth,
/// <summary>
/// Огонь.
/// </summary>
[PgName("fire")]
Fire,
/// <summary>
/// Дерево.
/// </summary>
[PgName("wood")]
Wood,
/// <summary>
/// Металл.
/// </summary>
[PgName("metal")]
Metal,
}
+33
View File
@@ -0,0 +1,33 @@
using NpgsqlTypes;
namespace Pf2e.Database.Model.Enum;
/// <summary>
/// Перечисление редкости сущности.
/// </summary>
public enum EntityRarity
{
/// <summary>
/// Обычная редкость.
/// </summary>
[PgName("common")]
Common,
/// <summary>
/// Необычная редкость.
/// </summary>
[PgName("uncommon")]
Uncommon,
/// <summary>
/// Редкая редкость.
/// </summary>
[PgName("rare")]
Rare,
/// <summary>
/// Уникальная редкость.
/// </summary>
[PgName("unique")]
Unique,
}
+135
View File
@@ -0,0 +1,135 @@
using NpgsqlTypes;
namespace Pf2e.Database.Model.Enum;
/// <summary>
/// Тип сущности.
/// </summary>
public enum EntityType
{
/// <summary>
/// Неизвестный тип сущности.
/// </summary>
[PgName("unknown")]
Unknown,
/// <summary>
/// Признак.
/// </summary>
[PgName("trait")]
Trait,
/// <summary>
/// Действие.
/// </summary>
[PgName("action")]
Action,
/// <summary>
/// Родословная.
/// </summary>
[PgName("ancestry")]
Ancestry,
/// <summary>
/// Архетип.
/// </summary>
[PgName("archetype")]
Archetype,
/// <summary>
/// Происхождение.
/// </summary>
[PgName("background")]
Background,
/// <summary>
/// Рюкзак.
/// </summary>
[PgName("backpack")]
Backpack,
/// <summary>
/// Способность кампании.
/// </summary>
[PgName("campaign_feature")]
CampaignFeature,
/// <summary>
/// Класс.
/// </summary>
[PgName("class")]
Class,
/// <summary>
/// Группа подклассов.
/// </summary>
[PgName("subclass_group")]
SubclassGroup,
/// <summary>
/// Подкласс.
/// </summary>
[PgName("subclass")]
Subclass,
/// <summary>
/// Состояние.
/// </summary>
[PgName("condition")]
Condition,
/// <summary>
/// Домен божества.
/// </summary>
[PgName("domain")]
Domain,
/// <summary>
/// Божество.
/// </summary>
[PgName("deity")]
Deity,
/// <summary>
/// Навык.
/// </summary>
[PgName("feat")]
Feat,
/// <summary>
/// Наследие.
/// </summary>
[PgName("heritage")]
Heritage,
/// <summary>
/// Заклианиние.
/// </summary>
[PgName("spell")]
Spell,
/// <summary>
/// Ритуал.
/// </summary>
[PgName("ritual")]
Ritual,
/// <summary>
/// Язык.
/// </summary>
[PgName("language")]
Language,
/// <summary>
/// Правило.
/// </summary>
[PgName("rule")]
Rule,
/// <summary>
/// Категория божества.
/// </summary>
[PgName("deity_category")]
DeityCategory,
}
+93
View File
@@ -0,0 +1,93 @@
using NpgsqlTypes;
namespace Pf2e.Database.Model.Enum;
/// <summary>
/// Категория черты.
/// </summary>
public enum FeatCategory
{
/// <summary>
/// Черта, которая не относится ни к одной из категорий.
/// </summary>
[PgName("uncategorized")]
Uncategorized,
/// <summary>
/// Черта наследия. Возможная коллизия между <see cref="Ancestry"/> и <see cref="AncestryFeature"/>.
/// </summary>
[PgName("ancestry")]
Ancestry,
/// <summary>
/// Черта наследия. Возможная коллизия между <see cref="Ancestry"/> и <see cref="AncestryFeature"/>.
/// </summary>
[PgName("ancestry-feature")]
AncestryFeature,
/// <summary>
/// Черта архетипа.
/// </summary>
[PgName("archetype")]
Archetype,
/// <summary>
/// Черта мифического призвания.
/// </summary>
[PgName("calling")]
Calling,
/// <summary>
/// Черта класса. Возможная коллизия между <see cref="ClassFeature"/> и <see cref="Class"/>.
/// </summary>
[PgName("class")]
Class,
/// <summary>
/// Черта класса. Возможная коллизия между <see cref="Class"/> и <see cref="ClassFeature"/>.
/// </summary>
[PgName("class-feature")]
ClassFeature,
/// <summary>
/// Черта навыка.
/// </summary>
[PgName("skill")]
Skill,
/// <summary>
/// Общая черта.
/// </summary>
[PgName("general")]
General,
/// <summary>
/// Бонусная черта.
/// </summary>
[PgName("bonus")]
Bonus,
/// <summary>
/// Божественный дар.
/// </summary>
[PgName("deityboon")]
Deityboon,
/// <summary>
/// Проклятие.
/// </summary>
[PgName("curse")]
Curse,
/// <summary>
/// Дар сообщества Pathfinder Society.
/// </summary>
[PgName("pfsboon")]
Pfsboon,
/// <summary>
/// Черта кампании.
/// </summary>
[PgName("campaign-feature")]
CampaignFeature,
}
+39
View File
@@ -0,0 +1,39 @@
using NpgsqlTypes;
namespace Pf2e.Database.Model.Enum;
/// <summary>
/// Размер.
/// </summary>
public enum SizeEnum
{
/// <summary>
/// Крошечный размер.
/// </summary>
[PgName("tiny")]
Tiny,
/// <summary>
/// Маленький размер.
/// </summary>
[PgName("small")]
Small,
/// <summary>
/// Средний размер.
/// </summary>
[PgName("medium")]
Medium,
/// <summary>
/// Большой размер.
/// </summary>
[PgName("large")]
Large,
/// <summary>
/// Громадный размер.
/// </summary>
[PgName("huge")]
Huge,
}
@@ -0,0 +1,51 @@
using NpgsqlTypes;
namespace Pf2e.Database.Model.Enum;
/// <summary>
/// Перечисление категорий источников.
/// </summary>
public enum SourceCategory
{
/// <summary>
/// Неизвестная категория источника.
/// </summary>
[PgName("rulebook")]
Rulebooks,
/// <summary>
/// Серия приключений.
/// </summary>
[PgName("adventure-path")]
AdventurePaths,
/// <summary>
/// Lost Omens
/// </summary>
[PgName("lost-omens")]
LostOmens,
/// <summary>
/// Приключение.
/// </summary>
[PgName("adventures")]
Adventures,
/// <summary>
/// Комиксы.
/// </summary>
[PgName("comics")]
Comics,
/// <summary>
/// Блог-посты.
/// </summary>
[PgName("blog-posts")]
BlogPosts,
/// <summary>
/// Сообщество.
/// </summary>
[PgName("society")]
Society,
}
+75
View File
@@ -0,0 +1,75 @@
using NpgsqlTypes;
namespace Pf2e.Database.Model.Enum;
/// <summary>
/// Тип области заклинания.
/// </summary>
public enum SpellAreaType
{
/// <summary>
/// Взрыв.
/// </summary>
[PgName("burst")]
Burst,
/// <summary>
/// Эманация.
/// </summary>
[PgName("emanation")]
Emanation,
/// <summary>
/// Конус.
/// </summary>
[PgName("cone")]
Cone,
/// <summary>
/// Линия.
/// </summary>
[PgName("line")]
Line,
/// <summary>
/// Квадрат.
/// </summary>
[PgName("square")]
Square,
/// <summary>
/// Куб.
/// </summary>
[PgName("cube")]
Cube,
/// <summary>
/// Цилиндр.
/// </summary>
[PgName("cylinder")]
Cylinder,
/// <summary>
/// Варьируемое.
/// </summary>
[PgName("varies")]
Varies,
/// <summary>
/// Параллелипипед.
/// </summary>
[PgName("cuboid")]
Cuboid,
/// <summary>
/// Круг.
/// </summary>
[PgName("circle")]
Circle,
/// <summary>
/// Иное.
/// </summary>
[PgName("other")]
Other,
}
@@ -0,0 +1,27 @@
using NpgsqlTypes;
namespace Pf2e.Database.Model.Enum;
/// <summary>
/// Тип урона заклинания.
/// </summary>
public enum SpellDamageCategory
{
/// <summary>
/// Обычный урон.
/// </summary>
[PgName("normal")]
Normal,
/// <summary>
/// Продолжительный урон.
/// </summary>
[PgName("persistent")]
Persistent,
/// <summary>
/// Брызги.
/// </summary>
[PgName("splash")]
Splash,
}
@@ -0,0 +1,27 @@
using NpgsqlTypes;
namespace Pf2e.Database.Model.Enum;
/// <summary>
/// Тип урона заклинания.
/// </summary>
public enum SpellDamageKinds
{
/// <summary>
/// Урон.
/// </summary>
[PgName("damage")]
Damage,
/// <summary>
/// Исцеление.
/// </summary>
[PgName("healing")]
Healing,
/// <summary>
/// Любое.
/// </summary>
[PgName("any")]
Any,
}
@@ -0,0 +1,51 @@
using NpgsqlTypes;
namespace Pf2e.Database.Model.Enum;
/// <summary>
/// Традиция заклинания.
/// </summary>
public enum SpellTradition
{
/// <summary>
/// Без традиции.
/// </summary>
[PgName("none")]
None,
/// <summary>
/// Сакральная традиция.
/// </summary>
[PgName("divine")]
Divine,
/// <summary>
/// Оккультная традиция.
/// </summary>
[PgName("occult")]
Occult,
/// <summary>
/// Арканная традиция.
/// </summary>
[PgName("arcane")]
Arcane,
/// <summary>
/// Примальная традиция.
/// </summary>
[PgName("primal")]
Primal,
/// <summary>
/// Элементная традиция.
/// </summary>
[PgName("elemental")]
Elemental,
/// <summary>
/// Все традиции.
/// </summary>
[PgName("all")]
All,
}
@@ -0,0 +1,27 @@
using NpgsqlTypes;
namespace Pf2e.Database.Model.Enum;
/// <summary>
/// Перечисление типа применения заклинаний.
/// </summary>
public enum SpellcastingType
{
/// <summary>
/// Не применяет заклинания.
/// </summary>
[PgName("none")]
None,
/// <summary>
/// Заготавливаемое заклинательство.
/// </summary>
[PgName("prepared")]
Prepared,
/// <summary>
/// Спонтанное заклинательство.
/// </summary>
[PgName("spontaneous")]
Spontaneous,
}
+45
View File
@@ -0,0 +1,45 @@
using NpgsqlTypes;
namespace Pf2e.Database.Model.Enum;
/// <summary>
/// Ранг тренировки.
/// </summary>
public enum TrainingRank
{
/// <summary>
/// Необученный.
/// </summary>
[PgName("untrained")]
Untrained,
/// <summary>
/// Обученный.
/// </summary>
[PgName("trained")]
Trained,
/// <summary>
/// Эксперт.
/// </summary>
[PgName("expert")]
Expert,
/// <summary>
/// Мастер.
/// </summary>
[PgName("master")]
Master,
/// <summary>
/// Легендарный.
/// </summary>
[PgName("legendary")]
Legendary,
/// <summary>
/// Мифический.
/// </summary>
[PgName("mythic")]
Mythic,
}
+45
View File
@@ -0,0 +1,45 @@
using NpgsqlTypes;
namespace Pf2e.Database.Model.Enum;
/// <summary>
/// Тип тренировки.
/// </summary>
public enum TrainingType
{
/// <summary>
/// Восприятие.
/// </summary>
[PgName("perception")]
Perception,
/// <summary>
/// Навык.
/// </summary>
[PgName("skill")]
Skill,
/// <summary>
/// Знание.
/// </summary>
[PgName("lore")]
Lore,
/// <summary>
/// Оружие.
/// </summary>
[PgName("weapon")]
Weapon,
/// <summary>
/// Защита.
/// </summary>
[PgName("armor")]
Armor,
/// <summary>
/// Спасбросок.
/// </summary>
[PgName("save")]
Save,
}
+33
View File
@@ -0,0 +1,33 @@
using NpgsqlTypes;
namespace Pf2e.Database.Model.Enum;
/// <summary>
/// Тип признака.
/// </summary>
public enum TraitType
{
/// <summary>
/// Обычный признак.
/// </summary>
[PgName("regular")]
Regular,
/// <summary>
/// Мировоззрение.
/// </summary>
[PgName("alignment")]
Alignment,
/// <summary>
/// Признак класса.
/// </summary>
[PgName("class")]
Class,
/// <summary>
/// Признак традиции.
/// </summary>
[PgName("tradition")]
Tradition,
}
+27
View File
@@ -0,0 +1,27 @@
using NpgsqlTypes;
namespace Pf2e.Database.Model.Enum;
/// <summary>
/// Перечисление типов зрения.
/// </summary>
public enum VisionEnum
{
/// <summary>
/// Обычное зрение.
/// </summary>
[PgName("normal")]
Normal,
/// <summary>
/// Зрение в темноте.
/// </summary>
[PgName("low-light-vision")]
LowLightVision,
/// <summary>
/// Тёмное зрение.
/// </summary>
[PgName("dark-vision")]
DarkVision,
}
+21
View File
@@ -0,0 +1,21 @@
using Newtonsoft.Json;
using Pf2e.Database.Model.Base;
namespace Pf2e.Database.Model;
/// <summary>
/// Интерфейс сущностей структуры.
/// </summary>
public interface IEntity
{
/// <summary>
/// Идентификатор сущности.
/// </summary>
public int Id { get; set; }
/// <summary>
/// Корневая сущность.
/// </summary>
[JsonIgnore]
public Entity Entity { get; set; }
}
+45
View File
@@ -0,0 +1,45 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
using Pf2e.Database.Model.Base;
using Pf2e.Database.Model.Enum;
namespace Pf2e.Database.Model.Structure;
/// <summary>
/// Действие.
/// </summary>
[EntityOfType(EntityType.Action)]
public class Action : IEntity
{
/// <inheritdoc/>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[Column(Order = 1)]
[Comment("Идентификатор")]
public int Id { get; set; }
/// <inheritdoc/>
public Entity Entity { get; set; } = null!;
/// <summary>
/// Количество действий.
/// </summary>
[Column(Order = 2)]
[Comment("Количество действий")]
public ActionEnum Actions { get; set; } = ActionEnum.Unknown;
/// <summary>
/// Категория действия.
/// </summary>
[Column(Order = 3)]
[Comment("Категория действия")]
public ActionCategory Category { get; set; } = ActionCategory.Unknown;
/// <summary>
/// Дополнительная информация.
/// </summary>
[Column(Order = 4)]
[Comment("Дополнительная информация")]
public string? Extra { get; set; }
}
+73
View File
@@ -0,0 +1,73 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
using Pf2e.Database.Model.Base;
using Pf2e.Database.Model.Enum;
using Pf2e.Database.Model.Structure.AncestryRelation;
namespace Pf2e.Database.Model.Structure;
/// <summary>
/// Родословная.
/// </summary>
[EntityOfType(EntityType.Ancestry)]
public class Ancestry : IEntity
{
/// <inheritdoc/>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[Column(Order = 1)]
[Comment("Идентификатор")]
public int Id { get; set; }
/// <inheritdoc/>
public Entity Entity { get; set; } = null!;
/// <summary>
/// Размеры родословной.
/// </summary>
[Column(Order = 2)]
[Comment("Размеры родословной")]
public SizeEnum[] Size { get; set; } = [];
/// <summary>
/// Количество очков здоровья.
/// </summary>
[Column(Order = 3)]
[Comment("Количество очков здоровья")]
public int Hp { get; set; }
/// <summary>
/// Скорость передвижения.
/// </summary>
[Column(Order = 4)]
[Comment("Скорость передвижения")]
public int Speed { get; set; }
/// <summary>
/// Тип зрения.
/// </summary>
[Column(Order = 5)]
[Comment("Тип зрения")]
public VisionEnum VisionEnum { get; set; } = VisionEnum.Normal;
/// <summary>
/// Коллекция повышениий аттрибутов.
/// </summary>
public ICollection<AncestryBoost> AncestryBoosts { get; set; } = [];
/// <summary>
/// Коллекция сущностей родословной.
/// </summary>
public ICollection<AncestryItem> AncestryItems { get; set; } = [];
/// <summary>
/// Коллекция языков родословной.
/// </summary>
public ICollection<AncestryLanguage> AncestryLanguages { get; set; } = [];
/// <summary>
/// Коллекция наследий.
/// </summary>
public ICollection<Heritage> Heritages { get; set; } = [];
}
@@ -0,0 +1,47 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
namespace Pf2e.Database.Model.Structure.AncestryRelation;
/// <summary>
/// Элемент набора повышений атрибутов для родословной.
/// </summary>
public class AncestryBoost
{
/// <summary>
/// Идентификатор элемента.
/// </summary>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Column(Order = 1)]
public int Id { get; set; }
/// <summary>
/// Идентификатор родословной, к которой относится этот элемент.
/// </summary>
[Column(Order = 2)]
[Comment("Идентификатор родословной")]
public int AncestryId { get; set; }
/// <summary>
/// Указывает, является ли этот элемент повышением атрибута. Если <c>false</c> - это снижение атрибута.
/// </summary>
[Column(Order = 3)]
[Comment("Флаг повышения/снижения")]
public bool Boost { get; set; } = true;
/// <summary>
/// Значение атрибута, которое повышается или понижается в этом элементе набора повышений атрибутов.
/// </summary>
[Column(Order = 4)]
[Comment("Значение атрибута")]
public Enum.AttributeValues Value { get; set; }
/// <summary>
/// Родословная, к которой относится этот элемент набора повышений атрибутов.
/// </summary>
[JsonIgnore]
public Ancestry Ancestry { get; set; } = null!;
}
@@ -0,0 +1,43 @@
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
using Pf2e.Database.Model.Base;
namespace Pf2e.Database.Model.Structure.AncestryRelation;
/// <summary>
/// Связь между родословной и сущностью.
/// </summary>
[PrimaryKey(nameof(AncestryId), nameof(ItemId))]
public class AncestryItem
{
/// <summary>
/// Идентификатор родословной.
/// </summary>
[Column(Order = 1)]
[Comment("Идентификатор родословной")]
public int AncestryId { get; set; }
/// <summary>
/// Идентификатор сущности, связанной с родословной.
/// </summary>
[Column(Order = 2)]
[Comment("Идентификатор связанной сущности")]
public int ItemId { get; set; }
/// <summary>
/// Уровень сущности в родословной.
/// </summary>
[Column(Order = 3)]
[Comment("Уровень сущности в родословной")]
public int Level { get; set; }
/// <summary>
/// Родословная, к которой относится сущность.
/// </summary>
public Ancestry Ancestry { get; set; } = null!;
/// <summary>
/// Сущность, связанная с родословной.
/// </summary>
public Entity Item { get; set; } = null!;
}
@@ -0,0 +1,42 @@
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
namespace Pf2e.Database.Model.Structure.AncestryRelation;
/// <summary>
/// Связь между родословной и языком.
/// </summary>
[PrimaryKey(nameof(AncestryId), nameof(LanguageId))]
public class AncestryLanguage
{
/// <summary>
/// Идентификатор родословной, к которой относится язык.
/// </summary>
[Column(Order = 1)]
[Comment("Идентификатор родословной")]
public int AncestryId { get; set; }
/// <summary>
/// Идентификатор языка, связанного с родословной.
/// </summary>
[Column(Order = 2)]
[Comment("Идентификатор связанного языка")]
public int LanguageId { get; set; }
/// <summary>
/// Является ли язык дополнительным для родословной.
/// </summary>
[Column(Order = 3)]
[Comment("Флаг дополнительного")]
public bool IsAdditional { get; set; }
/// <summary>
/// Родословная, к которой относится язык.
/// </summary>
public Ancestry Ancestry { get; set; } = null!;
/// <summary>
/// Язык, связанный с родословной.
/// </summary>
public Language Language { get; set; } = null!;
}
@@ -0,0 +1,55 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
using Pf2e.Database.Model.Base;
using Pf2e.Database.Model.Enum;
namespace Pf2e.Database.Model.Structure;
/// <summary>
/// Архетип.
/// </summary>
[EntityOfType(EntityType.Archetype)]
public class Archetype : IEntity
{
/// <inheritdoc/>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[Column(Order = 1)]
[Comment("Идентификатор")]
public int Id { get; set; }
/// <inheritdoc/>
public Entity Entity { get; set; } = null!;
/// <summary>
/// Уровень.
/// </summary>
[Column(Order = 2)]
[Comment("Уровень")]
public int Level { get; set; }
/// <summary>
/// Идентификатор способности, которая является посвящением в архетип.
/// </summary>
[Column(Order = 3)]
[Comment("ID посвящения")]
public int? DedicationFeatId { get; set; } = null;
/// <summary>
/// Дополнительная информация об архетипе.
/// </summary>
[Column(Order = 4)]
[Comment("Дополнительная информация")]
public string? Extra { get; set; }
/// <summary>
/// Посвящение в архетип.
/// </summary>
public Feat DedicationFeat { get; set; } = null!;
/// <summary>
/// Способности архетипа.
/// </summary>
public ICollection<Feat> Feats { get; set; } = [];
}
@@ -0,0 +1,22 @@
using Microsoft.EntityFrameworkCore;
namespace Pf2e.Database.Model.Structure.ArchetypeRelation;
/// <summary>
/// Связь между архетипом и его чертами.
/// </summary>
[PrimaryKey(nameof(ArchetypeId), nameof(FeatId))]
public class ArchetypeFeats
{
/// <summary>
/// Идентификатор архетипа, к которому относится черта.
/// </summary>
[Comment("Идентификатор архетипа")]
public int ArchetypeId { get; set; }
/// <summary>
/// Идентификатор черты, связанной с архетипом.
/// </summary>
[Comment("Идентификатор черты")]
public int FeatId { get; set; }
}
@@ -0,0 +1,40 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
using Pf2e.Database.Model.Base;
using Pf2e.Database.Model.Enum;
using Pf2e.Database.Model.Structure.BackgroundRelation;
namespace Pf2e.Database.Model.Structure;
/// <summary>
/// Предыстория.
/// </summary>
[EntityOfType(EntityType.Background)]
public class Background : IEntity
{
/// <inheritdoc/>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[Column(Order = 1)]
[Comment("Идентификатор")]
public int Id { get; set; }
/// <inheritdoc/>
public Entity Entity { get; set; } = null!;
/// <summary>
/// Аттрибуты предыстории.
/// </summary>
public ICollection<BackgroundBoost> BackgroundBoosts { get; set; } = [];
/// <summary>
/// Способности предыстории.
/// </summary>
public ICollection<BackgroundItem> BackgroundItems { get; set; } = [];
/// <summary>
/// Навыки предыстории.
/// </summary>
public ICollection<BackgroundTraining> BackgroundSkills { get; set; } = [];
}
@@ -0,0 +1,42 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
using Pf2e.Database.Model.Enum;
namespace Pf2e.Database.Model.Structure.BackgroundRelation;
/// <summary>
/// Элемент набора атрибутов для предыстории.
/// </summary>
public class BackgroundBoost
{
/// <summary>
/// Идентификатор элемента набора атрибутов для предыстории.
/// </summary>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Column(Order = 1)]
[Comment("Идентификатор")]
public int Id { get; set; }
/// <summary>
/// Идентификатор предыстории, к которой относится этот элемент набора атрибутов.
/// </summary>
[Column(Order = 2)]
[Comment("Идентификатор предыстории")]
public int BackgroundId { get; set; }
/// <summary>
/// Атрибут, к которому относится этот элемент набора атрибутов для предыстории.
/// </summary>
[Column(Order = 3)]
[Comment("Атрибут")]
public AttributeValues Attribute { get; set; }
/// <summary>
/// Предыстория, к которой относится этот элемент набора атрибутов.
/// </summary>
[JsonIgnore]
public Background Background { get; set; } = null!;
}
@@ -0,0 +1,44 @@
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
using Pf2e.Database.Model.Base;
namespace Pf2e.Database.Model.Structure.BackgroundRelation;
/// <summary>
/// Элемент предыстории, который может быть способностью, навыком или другим элементом, связанным с предысторией.
/// </summary>
[PrimaryKey(nameof(BackgroundId), nameof(EntityId))]
public class BackgroundItem
{
/// <summary>
/// Идентификатор элемента предыстории.
/// </summary>
[Column(Order = 1)]
[Comment("Идентификатор")]
public int BackgroundId { get; set; }
/// <summary>
/// Идентификатор сущности, связанной с предысторией.
/// </summary>
[Column(Order = 2)]
[Comment("Идентификатор сущности")]
public int EntityId { get; set; }
/// <summary>
/// Уровень элемента предыстории, если применимо. Может быть null, если уровень не указан или не применим.
/// </summary>
[Column(Order = 3)]
public int? Level { get; set; }
/// <summary>
/// Предыстория, к которой относится сущность.
/// </summary>
[JsonIgnore]
public Background Background { get; set; } = null!;
/// <summary>
/// Сущность, связанная с предысторией.
/// </summary>
public Entity Entity { get; set; } = null!;
}
@@ -0,0 +1,55 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
using Pf2e.Database.Model.Common;
using Pf2e.Database.Model.Enum;
namespace Pf2e.Database.Model.Structure.BackgroundRelation;
/// <summary>
/// Элемент набора связей между предысторией и тренируемым навыком.
/// </summary>
public class BackgroundTraining
{
/// <summary>
/// Идентификатор элемента набора связей между предысторией и тренируемым навыком.
/// </summary>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[Column(Order = 1)]
[Comment("Идентификатор")]
public int Id { get; set; }
/// <summary>
/// Идентификатор предыстории, к которой относится тренировка.
/// </summary>
[Column(Order = 2)]
[Comment("Идентификатор предыстории")]
public int BackgroundId { get; set; }
/// <summary>
/// Идентификатор тренируемого навыка.
/// </summary>
[Column(Order = 3)]
[Comment("Идентификатор тренируемого навыка")]
public int TrainingId { get; set; }
/// <summary>
/// Ранг тренировки, который получает персонаж при выборе данной предыстории.
/// </summary>
[Column(Order = 4)]
[Comment("Ранг тренировки")]
public TrainingRank Rank { get; set; }
/// <summary>
/// Предыстория, к которой относится тренировка.
/// </summary>
[JsonIgnore]
public Background Background { get; set; } = null!;
/// <summary>
/// Тренируемый навык, который получает персонаж при выборе данной предыстории.
/// </summary>
public Training Training { get; set; } = null!;
}
+73
View File
@@ -0,0 +1,73 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
using Pf2e.Database.Model.Base;
using Pf2e.Database.Model.Enum;
using Pf2e.Database.Model.Structure.ClassRelation;
namespace Pf2e.Database.Model.Structure;
/// <summary>
/// Класс.
/// </summary>
[EntityOfType(EntityType.Class)]
public class Class : IEntity
{
/// <inheritdoc/>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[Column(Order = 1)]
[Comment("Идентификатор")]
public int Id { get; set; }
/// <inheritdoc/>
public Entity Entity { get; set; } = null!;
/// <summary>
/// Количество очков здоровья.
/// </summary>
[Column(Order = 2)]
[Comment("Количество очков здоровья")]
public int Hp { get; set; }
/// <summary>
/// Указание на тип заклинательства.
/// </summary>
[Column(Order = 3)]
[Comment("Тип заклинательства")]
public SpellcastingType Spellcasting { get; set; } = SpellcastingType.None;
/// <summary>
/// Традиция заклинаний класса.
/// </summary>
[Column(Order = 6)]
[Comment("Традиция заклинаний")]
public SpellTradition? Tradition { get; set; } = null;
/// <summary>
/// Количество дополнительных навыков.
/// </summary>
[Column(Order = 7)]
[Comment("Дополнительные навыки")]
public int AdditionalTrainedSkills { get; set; }
/// <summary>
/// Аттрибуты класса.
/// </summary>
public virtual ICollection<ClassKeyAttribute> ClassKeyAbilities { get; set; } = [];
/// <summary>
/// Прогрессия класса.
/// </summary>
public virtual ICollection<ClassProgression> ClassProgressions { get; set; } = [];
/// <summary>
/// Навыки класса.
/// </summary>
public virtual ICollection<ClassTraining> ClassTrainings { get; set; } = [];
/// <summary>
/// Группы подклассов.
/// </summary>
public virtual ICollection<SubclassGroup> SubclassGroups { get; set; } = [];
}
@@ -0,0 +1,42 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
using Pf2e.Database.Model.Enum;
namespace Pf2e.Database.Model.Structure.ClassRelation;
/// <summary>
/// Набор ключевых атрибутов класса.
/// </summary>
public class ClassKeyAttribute
{
/// <summary>
/// Идентификатор ключевого атрибута класса.
/// </summary>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Column(Order = 1)]
[Comment("Идентификатор")]
public int Id { get; set; }
/// <summary>
/// Идентификатор класса, к которому относится ключевой атрибут.
/// </summary>
[Column(Order = 2)]
[Comment("Идентификатор класса")]
public int ClassId { get; set; }
/// <summary>
/// Ключевой атрибут класса.
/// </summary>
[Column(Order = 3)]
[Comment("Атрибут")]
public AttributeValues Value { get; set; }
/// <summary>
/// Класс, к которому относится ключевой атрибут.
/// </summary>
[JsonIgnore]
public Class Class { get; set; } = null!;
}
@@ -0,0 +1,62 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
using Pf2e.Database.Model.Base;
using Pf2e.Database.Model.Enum;
namespace Pf2e.Database.Model.Structure.ClassRelation;
/// <summary>
/// Элемент прогрессии класса.
/// </summary>
public class ClassProgression
{
/// <summary>
/// Идентификатор прогрессии класса.
/// </summary>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Column(Order = 1)]
[Comment("Идентификатор")]
public int Id { get; set; }
/// <summary>
/// Идентификатор класса, к которому относится элемент прогрессии.
/// </summary>
[Column(Order = 2)]
[Comment("Идентификатор класса")]
public int ClassId { get; set; }
/// <summary>
/// Уровень класса, на котором элемент прогрессии доступен.
/// </summary>
[Column(Order = 3)]
[Comment("Уровень класса")]
public int Level { get; set; }
/// <summary>
/// Тип элемента прогрессии класса.
/// </summary>
[Column(Order = 4)]
[Comment("Тип элемента прогрессии класса")]
public ClassProgressionType Type { get; set; }
/// <summary>
/// Идентификатор сущности, связанной с элементом прогрессии класса (например, навык, атрибут и т.д.).
/// </summary>
[Column(Order = 5)]
[Comment("Идентификатор сущности")]
public int? EntityId { get; set; }
/// <summary>
/// Класс, к которому относится элемент прогрессии.
/// </summary>
[JsonIgnore]
public Class Class { get; set; } = null!;
/// <summary>
/// Сущность, связанная с элементом прогрессии класса (например, навык, атрибут и т.д.).
/// </summary>
public Entity? Entity { get; set; }
}
@@ -0,0 +1,55 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
using Pf2e.Database.Model.Common;
using Pf2e.Database.Model.Enum;
namespace Pf2e.Database.Model.Structure.ClassRelation;
/// <summary>
/// Набор элементов, связывающий класс и тренируемый навык.
/// </summary>
public class ClassTraining
{
/// <summary>
/// Идентификатор набора элементов, связывающий класс и тренируемый навык.
/// </summary>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Column(Order = 1)]
[Comment("Идентификатор")]
public int Id { get; set; }
/// <summary>
/// Идентификатор класса, к которому относится тренировка.
/// </summary>
[Column(Order = 2)]
[Comment("Идентификатор класса")]
public int ClassId { get; set; }
/// <summary>
/// Идентификатор тренируемого навыка, к которому относится тренировка.
/// </summary>
[Column(Order = 3)]
[Comment("Идентификатор тренируемого навыка")]
public int? TrainingId { get; set; }
/// <summary>
/// Ранг тренировки, определяющий уровень владения навыком.
/// </summary>
[Column(Order = 4)]
[Comment("Ранг тренировки")]
public TrainingRank Rank { get; set; }
/// <summary>
/// Класс, к которому относится тренировка.
/// </summary>
[JsonIgnore]
public Class Class { get; set; } = null!;
/// <summary>
/// Тренируемый навык, к которому относится тренировка.
/// </summary>
public Training? Training { get; set; }
}
@@ -0,0 +1,20 @@
using Microsoft.EntityFrameworkCore;
namespace Pf2e.Database.Model.Structure.ClassRelation;
/// <summary>
/// Элемент, связывающий сущность с подклассом.
/// </summary>
[PrimaryKey(nameof(SubclassId), nameof(EntityId))]
public class SubclassProvidedEntities
{
/// <summary>
/// Идентификатор подкласса, к которому относится сущность.
/// </summary>
public int SubclassId { get; set; }
/// <summary>
/// Идентификатор сущности, предоставляемой подклассом.
/// </summary>
public int EntityId { get; set; }
}
@@ -0,0 +1,53 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
using Pf2e.Database.Model.Common;
using Pf2e.Database.Model.Enum;
namespace Pf2e.Database.Model.Structure.ClassRelation;
/// <summary>
/// Связь между подклассами и тренировками.
/// </summary>
public class SubclassTrainings
{
/// <summary>
/// Идентификатор связи между подклассом и тренировкой.
/// </summary>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Column(Order = 1)]
[Comment("Идентификатор связи")]
public int Id { get; set; }
/// <summary>
/// Идентификатор подкласса, к которому относится тренировка.
/// </summary>
[Column(Order = 2)]
[Comment("Идентификатор подкласса")]
public int SubclassId { get; set; }
/// <summary>
/// Идентификатор тренировки, связанной с подклассом.
/// </summary>
[Column(Order = 3)]
[Comment("Идентификатор тренировки")]
public int TrainingId { get; set; }
/// <summary>
/// Ранг тренировки для данного подкласса.
/// </summary>
[Column(Order = 4)]
[Comment("Ранг тренировки")]
public TrainingRank Rank { get; set; } = TrainingRank.Trained;
/// <summary>
/// Подкласс, к которому относится тренировка.
/// </summary>
public Subclass Subclass { get; set; } = null!;
/// <summary>
/// Тренировка, связанная с подклассом.
/// </summary>
public Training Training { get; set; } = null!;
}
@@ -0,0 +1,38 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
using Pf2e.Database.Model.Base;
using Pf2e.Database.Model.Enum;
namespace Pf2e.Database.Model.Structure;
/// <summary>
/// Состояние.
/// </summary>
[EntityOfType(EntityType.Condition)]
public class Condition : IEntity
{
/// <inheritdoc/>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[Column(Order = 1)]
[Comment("Идентификатор")]
public int Id { get; set; }
/// <inheritdoc/>
public Entity Entity { get; set; } = null!;
/// <summary>
/// Группа состояния.
/// </summary>
[Column(Order = 2)]
[Comment("Группа состояния")]
public ConditionGroup? Group { get; set; }
/// <summary>
/// Дополнительная информация.
/// </summary>
[Column(Order = 3)]
[Comment("Дополнительная информация")]
public string? Extra { get; set; }
}
+134
View File
@@ -0,0 +1,134 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
using Pf2e.Database.Model.Base;
using Pf2e.Database.Model.Common;
using Pf2e.Database.Model.Common.I18n;
using Pf2e.Database.Model.Enum;
using Pf2e.Database.Model.Structure.DeityRelation;
namespace Pf2e.Database.Model.Structure;
/// <summary>
/// Божество.
/// </summary>
[EntityOfType(EntityType.Deity)]
public class Deity : IEntity
{
/// <inheritdoc/>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[Column(Order = 1)]
[Comment("Идентификатор")]
public int Id { get; set; }
/// <inheritdoc/>
public Entity Entity { get; set; } = null!;
/// <summary>
/// Тип божества.
/// </summary>
[Column(Order = 2)]
[Comment("Тип божества")]
public DeityType Type { get; set; }
/// <summary>
/// Категория божества.
/// </summary>
[Column(Order = 3)]
[Comment("Категория божества")]
public string Category { get; set; } = null!;
/// <summary>
/// Тип божественный силы.
/// </summary>
[Column(Order = 4)]
[Comment("Тип божественной силы")]
public DeityFonts Fonts { get; set; }
/// <summary>
/// Тип освящения.
/// </summary>
[Column(Order = 5)]
[Comment("Тип освящения")]
public DeitySanctification Sanctification { get; set; }
/// <summary>
/// Обязательное освящение.
/// </summary>
[Column(Order = 6)]
[Comment("Обязательное освящение")]
public bool SanctificationMust { get; set; }
/// <summary>
/// Заветы божества.
/// </summary>
[Column(Order = 7)]
[Comment("Заветы божества")]
public LocaleString Edicts { get; set; } = [];
/// <summary>
/// Анафемы божества.
/// </summary>
[Column(Order = 8)]
[Comment("Анафемы божества")]
public LocaleString Anathema { get; set; } = [];
/// <summary>
/// Религиозный символ божества.
/// </summary>
[Column(Order = 9)]
[Comment("Религиозный символ божества")]
public LocaleString ReligiousSymbol { get; set; } = [];
/// <summary>
/// Священное животное божества.
/// </summary>
[Column(Order = 10)]
[Comment("Священное животное божества")]
public LocaleString SacredAnimal { get; set; } = [];
/// <summary>
/// Область покровительства божества.
/// </summary>
[Column(Order = 11)]
[Comment("Область покровительства божества")]
public LocaleString AreaOfConcern { get; set; } = [];
/// <summary>
/// Атрибуты божества.
/// </summary>
public ICollection<DeityAttribute> DeityAttributes { get; set; } = new List<DeityAttribute>();
/// <summary>
/// Домены божества.
/// </summary>
public ICollection<DeityDomain> DeityDomains { get; set; } = new List<DeityDomain>();
/// <summary>
/// Заклинания божества.
/// </summary>
public ICollection<DeitySpell> DeitySpells { get; set; } = new List<DeitySpell>();
/// <summary>
/// Оружия божества.
/// </summary>
public ICollection<DeityWeapon> DeityWeapons { get; set; } = new List<DeityWeapon>();
/// <summary>
/// Категория божества (связь).
/// </summary>
public DeityCategory? CategoryEntity { get; set; }
/// <summary>
/// Идентификатор категории божества.
/// </summary>
[Column(Order = 12)]
[Comment("Идентификатор категории божества")]
public int? CategoryId { get; set; }
/// <summary>
/// Навыки божества.
/// </summary>
public ICollection<Training> Skills { get; set; } = new List<Training>();
}
@@ -0,0 +1,32 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
using Pf2e.Database.Model.Base;
using Pf2e.Database.Model.Common.I18n;
using Pf2e.Database.Model.Enum;
namespace Pf2e.Database.Model.Structure;
/// <summary>
/// Категория божества.
/// </summary>
[EntityOfType(EntityType.DeityCategory)]
public class DeityCategory : IEntity
{
/// <inheritdoc/>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[Column(Order = 1)]
[Comment("Идентификатор")]
public int Id { get; set; }
/// <inheritdoc/>
public Entity Entity { get; set; } = null!;
/// <summary>
/// Описание категории.
/// </summary>
[Column(Order = 2)]
[Comment("Описание категории")]
public LocaleString Description { get; set; } = [];
}
@@ -0,0 +1,40 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
using Pf2e.Database.Model.Enum;
namespace Pf2e.Database.Model.Structure.DeityRelation;
/// <summary>
/// Элемент отношения божества и атрибута.
/// </summary>
public class DeityAttribute
{
/// <summary>
/// Идентификатор.
/// </summary>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Column(Order = 1)]
[Comment("Идентификатор")]
public int Id { get; set; }
/// <summary>
/// Идентификатор божества.
/// </summary>
[Column(Order = 2)]
[Comment("Идентификатор божества")]
public int DeityId { get; set; }
/// <summary>
/// Атрибут божества.
/// </summary>
[Column(Order = 3)]
[Comment("Атрибут божества")]
public AttributeValues Attribute { get; set; }
/// <summary>
/// Божество, к которому относится атрибут.
/// </summary>
public Deity Deity { get; set; } = null!;
}
@@ -0,0 +1,59 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
using Pf2e.Database.Model.Structure;
namespace Pf2e.Database.Model.Structure.DeityRelation;
/// <summary>
/// Элемент отношения божества и домена.
/// </summary>
public class DeityDomain
{
/// <summary>
/// Идентификатор отношения божества и домена.
/// </summary>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Column(Order = 1)]
[Comment("Идентификатор")]
public int Id { get; set; }
/// <summary>
/// Идентификатор божества, к которому относится домен.
/// </summary>
[Column(Order = 2)]
[Comment("Идентификатор божества")]
public int DeityId { get; set; }
/// <summary>
/// Идентификатор домена.
/// </summary>
[Column(Order = 4)]
[Comment("Идентификатор домена")]
public int DomainId { get; set; }
/// <summary>
/// Флаг, указывающий, является ли домен основным для божества.
/// </summary>
[Column(Order = 3)]
[Comment("Флаг основного домена")]
public bool IsPrimary { get; set; }
/// <summary>
/// Значение домена божества, например, "Свет", "Тьма" и т.д.
/// </summary>
[Column(Order = 5)]
[MaxLength(Pf2eDbContext.NameLength)]
public string Value { get; set; } = null!;
/// <summary>
/// Божество, к которому относится домен.
/// </summary>
public virtual Deity Deity { get; set; } = null!;
/// <summary>
/// Домен, связанный с божеством.
/// </summary>
public virtual Domain Domain { get; set; } = null!;
}
@@ -0,0 +1,51 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
namespace Pf2e.Database.Model.Structure.DeityRelation;
/// <summary>
/// Связь божества и заклинания.
/// </summary>
public class DeitySpell
{
/// <summary>
/// Идентификатор связи божества и заклинания.
/// </summary>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Column(Order = 1)]
[Comment("Идентификатор")]
public int Id { get; set; }
/// <summary>
/// Идентификатор божества, к которому относится заклинание.
/// </summary>
[Column(Order = 2)]
[Comment("Идентификатор божества")]
public int DeityId { get; set; }
/// <summary>
/// Уровень заклинания.
/// </summary>
[Column(Order = 3)]
[Comment("Уровень заклинания")]
public int Level { get; set; }
/// <summary>
/// Идентификатор заклинания, связанного с божеством.
/// </summary>
[Column(Order = 4)]
[Comment("Идентификатор заклинания")]
public int SpellId { get; set; }
/// <summary>
/// Божество, к которому относится заклинание.
/// </summary>
public virtual Deity Deity { get; set; } = null!;
/// <summary>
/// Заклинание, связанное с божеством.
/// </summary>
public virtual Spell Spell { get; set; } = null!;
}
@@ -0,0 +1,40 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
namespace Pf2e.Database.Model.Structure.DeityRelation;
/// <summary>
/// Связь божества с оружием.
/// </summary>
public class DeityWeapon
{
/// <summary>
/// Идентификатор связи божества с оружием.
/// </summary>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Column(Order = 1)]
[Comment("Идентификатор")]
public int Id { get; set; }
/// <summary>
/// Идентификатор божества, к которому относится оружие.
/// </summary>
[Column(Order = 2)]
[Comment("Идентификатор божества")]
public int DeityId { get; set; }
/// <summary>
/// Название оружия, связанного с божеством. Будет изменено на идентификатор оружия в будущем, когда будет реализована система оружия.
/// </summary>
[Column(Order = 3)]
[MaxLength(Pf2eDbContext.NameLength)]
[Comment("Название оружия")]
public string Weapon { get; set; } = null!;
/// <summary>
/// Божество, к которому относится оружие.
/// </summary>
public virtual Deity Deity { get; set; } = null!;
}
+78
View File
@@ -0,0 +1,78 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
using Pf2e.Database.Model.Base;
using Pf2e.Database.Model.Enum;
using Pf2e.Database.Model.Structure.DeityRelation;
namespace Pf2e.Database.Model.Structure;
/// <summary>
/// Домен.
/// </summary>
[EntityOfType(EntityType.Domain)]
public class Domain : IEntity
{
/// <inheritdoc/>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[Column(Order = 1)]
[Comment("Идентификатор")]
public int Id { get; set; }
/// <inheritdoc/>
public Entity Entity { get; set; } = null!;
/// <summary>
/// Идентификатор заклинания домена.
/// </summary>
[Column(Order = 2)]
[Comment("Заклинание домена")]
public int DomainSpellId { get; set; }
/// <summary>
/// Идентификатор продвинутого заклинания домена.
/// </summary>
[Column(Order = 3)]
[Comment("Продвинутое заклинание домена")]
public int AdvancedDomainSpellId { get; set; }
/// <summary>
/// Идентификатор апокрифического заклинания домена.
/// </summary>
[Column(Order = 4)]
[Comment("Апокрифическое заклинание домена")]
public int? ApocryphalSpellId { get; set; }
/// <summary>
/// Идентификатор продвинутого апокрифического заклинания домена.
/// </summary>
[Column(Order = 5)]
[Comment("Продвинутое апокрифическое заклинание домена")]
public int? AdvancedApocryphalSpellId { get; set; }
/// <summary>
/// Заклинание домена.
/// </summary>
public virtual Spell DomainSpell { get; set; } = null!;
/// <summary>
/// Продвинутое заклинание домена.
/// </summary>
public virtual Spell AdvancedDomainSpell { get; set; } = null!;
/// <summary>
/// Апокрифическое заклинание домена.
/// </summary>
public virtual Spell? ApocryphalSpell { get; set; }
/// <summary>
/// Продвинутое апокрифическое заклинание домена.
/// </summary>
public virtual Spell? AdvancedApocryphalSpell { get; set; }
/// <summary>
/// Связи с божествами, которые используют этот домен.
/// </summary>
public virtual ICollection<DeityDomain> DeityDomains { get; set; } = new List<DeityDomain>();
}
+63
View File
@@ -0,0 +1,63 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
using Pf2e.Database.Model.Base;
using Pf2e.Database.Model.Enum;
using Pf2e.Database.Model.Structure.FeatRelation;
namespace Pf2e.Database.Model.Structure;
/// <summary>
/// Способность.
/// </summary>
[EntityOfType(EntityType.Feat)]
public class Feat : IEntity
{
/// <inheritdoc/>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[Column(Order = 1)]
[Comment("Идентификатор")]
public int Id { get; set; }
/// <inheritdoc/>
public Entity Entity { get; set; } = null!;
/// <summary>
/// Категория способности.
/// </summary>
[Column(Order = 3)]
[Comment("Категория способности")]
public FeatCategory Category { get; set; }
/// <summary>
/// Уровень способности.
/// </summary>
[Column(Order = 4)]
[Comment("Уровень способности")]
public int Level { get; set; }
/// <summary>
/// Количество действий, необходимых для применения фита.
/// </summary>
[Column(Order = 5)]
[Comment("Количество действий")]
public ActionEnum Actions { get; set; } = ActionEnum.Other;
/// <summary>
/// Дополнительная информация.
/// </summary>
[Column(Order = 6)]
[Comment("Дополнительная информация")]
public string? Extra { get; set; }
/// <summary>
/// Архетипы, которые могут использовать эту способность.
/// </summary>
public ICollection<Archetype> Archetypes { get; set; } = [];
/// <summary>
/// Требования к получению способности.
/// </summary>
public ICollection<FeatPrerequisite> FeatPrerequisites { get; set; } = [];
}
@@ -0,0 +1,40 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
namespace Pf2e.Database.Model.Structure.FeatRelation;
/// <summary>
/// Предварительное требование для черты.
/// </summary>
public class FeatPrerequisite
{
/// <summary>
/// Идентификатор предварительного требования для чериы.
/// </summary>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Column(Order = 1)]
[Comment("Идентификатор")]
public int Id { get; set; }
/// <summary>
/// Идентификатор черты, к которой относится предварительное требование.
/// </summary>
[Column(Order = 2)]
[Comment("Идентификатор черты")]
public int FeatId { get; set; }
/// <summary>
/// Предварительное требование для черты.
/// </summary>
[Column(Order = 3)]
[MaxLength(255)]
[Comment("Предварительное требование")]
public string Value { get; set; } = null!;
/// <summary>
/// Черта, к которой относится предварительное требование.
/// </summary>
public virtual Feat Feat { get; set; } = null!;
}
+36
View File
@@ -0,0 +1,36 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
using Pf2e.Database.Model.Base;
using Pf2e.Database.Model.Enum;
namespace Pf2e.Database.Model.Structure;
/// <summary>
/// Наследие.
/// </summary>
[EntityOfType(EntityType.Heritage)]
public class Heritage : IEntity
{
/// <inheritdoc/>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[Column(Order = 1)]
[Comment("Идентификатор")]
public int Id { get; set; }
/// <inheritdoc/>
public Entity Entity { get; set; } = null!;
/// <summary>
/// Идентификатор родословной.
/// </summary>
[Column(Order = 2)]
[Comment("Идентификатор родословной")]
public int? AncestryId { get; set; }
/// <summary>
/// Родословная.
/// </summary>
public Ancestry? Ancestry { get; set; }
}
+24
View File
@@ -0,0 +1,24 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
using Pf2e.Database.Model.Base;
using Pf2e.Database.Model.Enum;
namespace Pf2e.Database.Model.Structure;
/// <summary>
/// Язык.
/// </summary>
[EntityOfType(EntityType.Language)]
public class Language : IEntity
{
/// <inheritdoc/>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[Column(Order = 1)]
[Comment("Идентификатор")]
public int Id { get; set; }
/// <inheritdoc/>
public Entity Entity { get; set; } = null!;
}
+134
View File
@@ -0,0 +1,134 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
using Pf2e.Database.Model.Base;
using Pf2e.Database.Model.Common.I18n;
using Pf2e.Database.Model.Enum;
using Pf2e.Database.Model.Structure.RitualRelation;
namespace Pf2e.Database.Model.Structure;
/// <summary>
/// Ритуал.
/// </summary>
[EntityOfType(EntityType.Ritual)]
public class Ritual : IEntity
{
/// <inheritdoc/>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[Column(Order = 1)]
[Comment("Идентификатор")]
public int Id { get; set; }
/// <inheritdoc/>
public Entity Entity { get; set; } = null!;
/// <summary>
/// Уровень ритуала.
/// </summary>
[Column(Order = 2)]
[Comment("Уровень ритуала")]
public int Level { get; set; }
/// <summary>
/// Продолжительность ритуала в секундах.
/// </summary>
[Column(Order = 3)]
[Comment("Продолжительность ритуала в секундах")]
public int CastTime { get; set; }
/// <summary>
/// Время действия эффекта ритуала в секундах.
/// </summary>
[Column(Order = 4)]
[Comment("Время действия эффекта ритуала в секундах")]
public int Duration { get; set; }
/// <summary>
/// Элементы ритуала.
/// </summary>
[Column(Order = 5)]
[Comment("Элементы ритуала")]
public ElementEnum[] Elements { get; set; } = [];
/// <summary>
/// Дальнность ритуала в футах.
/// </summary>
[Column(Order = 6)]
[Comment("Дальнность ритуала")]
public int Range { get; set; }
/// <summary>
/// Ранг основной проверки.
/// </summary>
[Column(Order = 7)]
[Comment("Ранг основной проверки")]
public TrainingRank PrimaryCheckRank { get; set; } = TrainingRank.Trained;
/// <summary>
/// Дополнительная информация об основной проверки.
/// </summary>
[Column(Order = 8)]
[Comment("Дополнительная информация об основной проверки")]
public LocaleString? PrimaryCheckSuffix { get; set; } = null;
/// <summary>
/// Количество дополнительных заклинателей, необходимых для ритуала.
/// </summary>
[Column(Order = 9)]
[Comment("Количество дополнительных заклинателей")]
public int SecondaryCasters { get; set; } = 0;
/// <summary>
/// Максимальное количество дополнительных заклинателей, необходимых для ритуала.
/// </summary>
[Column(Order = 10)]
[Comment("Максимальное количество дополнительных заклинателей")]
public int SecondaryCastersMax { get; set; } = 0;
/// <summary>
/// Дополнительная информация о дополнительных заклинателях.
/// </summary>
[Column(Order = 11)]
[Comment("Дополнительная информация о дополнительных заклинателях")]
public LocaleString? SecondaryCastersSuffix { get; set; } = null;
/// <summary>
/// Строка, описывающая дальность ритуала.
/// </summary>
[Column(Order = 12)]
[Comment("Дальность ритуала")]
public LocaleString? RangeString { get; set; } = null;
/// <summary>
/// Строка, описывающая цель ритуала.
/// </summary>
[Column(Order = 13)]
[Comment("Цель ритуала")]
public LocaleString? Target { get; set; } = null;
/// <summary>
/// Строка, описывающая стоимость ритуала.
/// </summary>
[Column(Order = 14)]
[Comment("Стоимость ритуала")]
public LocaleString? Cost { get; set; } = null;
/// <summary>
/// Строка, описывающая требования к ритуалу.
/// </summary>
[Column(Order = 15)]
[Comment("Требования к ритуалу")]
public LocaleString? Requirement { get; set; } = null;
/// <summary>
/// Коллекция областей ритуала.
/// </summary>
public RitualArea? Area { get; set; } = null;
/// <summary>
/// Коллекция проверок ритуала.
/// </summary>
public ICollection<RitualCheck> Checks { get; set; } = [];
}
@@ -0,0 +1,42 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
using Pf2e.Database.Model.Common.I18n;
using Pf2e.Database.Model.Enum;
namespace Pf2e.Database.Model.Structure.RitualRelation;
/// <summary>
/// Область действия ритуала.
/// </summary>
public class RitualArea
{
/// <summary>
/// Идентификатор ритуала, к которому относится область действия.
/// </summary>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[Column(Order = 1)]
[Comment("Идентификатор ритуала")]
public int RitualId { get; set; }
/// <summary>
/// Описание области действия ритуала.
/// </summary>
[Column(Order = 2)]
[MaxLength(255)]
[Comment("Описание области действия ритуала")]
public LocaleString? Details { get; set; }
/// <summary>
/// Тип области действия ритуала.
/// </summary>
[Column(Order = 3)]
[Comment("Тип области действия ритуала")]
public SpellAreaType Type { get; set; }
/// <summary>
/// Ритуал, к которому относится область действия.
/// </summary>
public virtual Ritual Ritual { get; set; } = null!;
}
@@ -0,0 +1,68 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
using Pf2e.Database.Model.Common;
using Pf2e.Database.Model.Common.I18n;
using Pf2e.Database.Model.Enum;
namespace Pf2e.Database.Model.Structure.RitualRelation;
/// <summary>
/// Проверка для ритуала.
/// </summary>
public class RitualCheck
{
/// <summary>
/// Идентификатор проверки ритуала.
/// </summary>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Column(Order = 1)]
[Comment("Идентификатор")]
public int Id { get; set; }
/// <summary>
/// Идентификатор ритуала, к которому относится проверка.
/// </summary>
[Column(Order = 2)]
[Comment("Идентификатор ритуала")]
public int RitualId { get; set; }
/// <summary>
/// Идентификатор тренировки, связанной с проверкой ритуала.
/// </summary>
[Column(Order = 3)]
[Comment("Идентификатор тренировки")]
public int? TrainingId { get; set; }
/// <summary>
/// Дополнение к проверке.
/// </summary>
[Column(Order = 4)]
[Comment("Дополнение к проверке")]
public LocaleString? TrainingString { get; set; } = null;
/// <summary>
/// Ранг тренировки, связанной с проверкой ритуала.
/// </summary>
[Column(Order = 5)]
[Comment("Ранг тренировки")]
public TrainingRank Rank { get; set; }
/// <summary>
/// Флаг, указывающий, является ли проверка основной для ритуала.
/// </summary>
[Column(Order = 6)]
[Comment("Флаг основной проверки")]
public bool IsPrimary { get; set; }
/// <summary>
/// Ритуал, к которому относится проверка.
/// </summary>
public Ritual Ritual { get; set; } = null!;
/// <summary>
/// Тренировка, связанная с проверкой ритуала.
/// </summary>
public Training? Training { get; set; } = null!;
}
+53
View File
@@ -0,0 +1,53 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
using Pf2e.Database.Model.Base;
using Pf2e.Database.Model.Enum;
namespace Pf2e.Database.Model.Structure;
/// <summary>
/// Правило.
/// </summary>
[EntityOfType(EntityType.Rule)]
public class Rule : IEntity
{
/// <inheritdoc/>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[Column(Order = 1)]
[Comment("Идентификатор")]
public int Id { get; set; }
/// <inheritdoc/>
public Entity Entity { get; set; } = null!;
/// <summary>
/// Порядок отображения.
/// </summary>
[Column(Order = 2)]
[Comment("Порядок отображения")]
public int Order { get; set; }
/// <summary>
/// Идентификатор родительского правила.
/// </summary>
[Column(Order = 3)]
[Comment("Родительского правило")]
public int? ParentId { get; set; } = null;
/// <summary>
/// Родительское правило.
/// </summary>
public Rule? Parent { get; set; } = null;
/// <summary>
/// Дочерние правила.
/// </summary>
public ICollection<Rule> Children { get; set; } = [];
/// <summary>
/// Хлебные крошки для отображения в интерфейсе.
/// </summary>
public string[] Breadcrumbs { get; set; } = [];
}
+140
View File
@@ -0,0 +1,140 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
using Pf2e.Database.Model.Base;
using Pf2e.Database.Model.Common.I18n;
using Pf2e.Database.Model.Enum;
using Pf2e.Database.Model.Structure.SpellRelation;
namespace Pf2e.Database.Model.Structure;
/// <summary>
/// Заклинание.
/// </summary>
[EntityOfType(EntityType.Spell)]
public class Spell : IEntity
{
/// <inheritdoc/>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[Column(Order = 1)]
[Comment("Идентификатор")]
public int Id { get; set; }
/// <inheritdoc/>
public Entity Entity { get; set; } = null!;
/// <summary>
/// Уровень заклинания.
/// </summary>
[Column(Order = 2)]
public int Level { get; set; }
/// <summary>
/// Является ли заклинание фокусом.
/// </summary>
[Column(Order = 3)]
[Comment("Фокус?")]
public bool IsFocus { get; set; }
/// <summary>
/// Количество действий, необходимых для применения заклинания.
/// </summary>
[Column(Order = 4)]
[Comment("Количество действий")]
public ActionEnum Actions { get; set; } = ActionEnum.Other;
/// <summary>
/// Время длительности заклинания в секундах.
/// </summary>
[Column(Order = 5)]
[MaxLength(255)]
[Comment("Время длительности заклинания")]
public int? Duration { get; set; } = null;
/// <summary>
/// Дальность заклинания в футах.
/// </summary>
[Column(Order = 6)]
[Comment("Дальность заклинания")]
public int Range { get; set; }
/// <summary>
/// Традиции заклинания.
/// </summary>
[Column(Order = 7)]
[Comment("Традиции заклинания")]
public SpellTradition[] Traditions { get; set; } = [];
/// <summary>
/// Элементы заклинания.
/// </summary>
[Column(Order = 8)]
[Comment("Элементы заклинания")]
public ElementEnum[] Elements { get; set; } = [];
/// <summary>
/// Триггер заклинания.
/// </summary>
[Column(Order = 9)]
[Comment("Триггер заклинания")]
public LocaleString? Trigger { get; set; } = null;
/// <summary>
/// Цель заклинания.
/// </summary>
[Column(Order = 10)]
[Comment("Цель заклинания")]
public LocaleString? Target { get; set; } = null;
/// <summary>
/// Стоимость заклинания.
/// </summary>
[Column(Order = 11)]
[Comment("Стоимость заклинания")]
public LocaleString? Cost { get; set; } = null;
/// <summary>
/// Требования к заклинанию.
/// </summary>
[Column(Order = 12)]
[Comment("Требования к заклинанию")]
public LocaleString? Requirement { get; set; } = null;
/*
[Column(Order = 4)]
public bool Counteraction { get; set; }
[Column(Order = 5)]
public bool Sustained { get; set; }
[Column(Order = 7)]
[MaxLength(255)]
public string Range { get; set; } = null!;
[Column(Order = 8)]
[MaxLength(255)]
public string Requirements { get; set; } = null!;
[Column(Order = 9)]
[MaxLength(255)]
public string Target { get; set; } = null!;
[Column(Order = 10)]
[MaxLength(255)]
public string Time { get; set; } = null!;
[Column(Order = 11)]
public bool CanHeightening { get; set; }
*/
/// <summary>
/// Коллекция области заклинания.
/// </summary>
public ICollection<SpellArea> SpellAreas { get; set; } = [];
/// <summary>
/// Коллекция защиты от заклинания.
/// </summary>
public ICollection<SpellDefense> SpellDefenses { get; set; } = new List<SpellDefense>();
}
@@ -0,0 +1,49 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
using Pf2e.Database.Model.Common.I18n;
using Pf2e.Database.Model.Enum;
namespace Pf2e.Database.Model.Structure.SpellRelation;
/// <summary>
/// Область заклинания.
/// </summary>
public class SpellArea
{
/// <summary>
/// Идентификатор области заклинания.
/// </summary>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Column(Order = 1)]
[Comment("Идентификатор")]
public int Id { get; set; }
/// <summary>
/// Идентификатор заклинания, к которому относится область.
/// </summary>
[Column(Order = 2)]
[Comment("Идентификатор заклинания")]
public int SpellId { get; set; }
/// <summary>
/// Описание области заклинания.
/// </summary>
[Column(Order = 3)]
[MaxLength(255)]
[Comment("Описание области заклинания")]
public LocaleString? Details { get; set; }
/// <summary>
/// Тип области заклинания.
/// </summary>
[Column(Order = 4)]
[Comment("Тип области заклинания")]
public SpellAreaType Type { get; set; }
/// <summary>
/// Заклинание, к которому относится область.
/// </summary>
public virtual Spell Spell { get; set; } = null!;
}
@@ -0,0 +1,86 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
using Pf2e.Database.Model.Enum;
namespace Pf2e.Database.Model.Structure.SpellRelation;
/// <summary>
/// Урон заклинания.
/// </summary>
public class SpellDamage
{
/// <summary>
/// Идентификатор урона заклинания.
/// </summary>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Column(Order = 1)]
[Comment("Идентификатор урона заклинания")]
public int Id { get; set; }
/// <summary>
/// Идентификатор заклинания, к которому относится урон.
/// </summary>
[Column(Order = 2)]
[Comment("Идентификатор заклинания")]
public int SpellId { get; set; }
/// <summary>
/// Уровень заклинания, к которому относится урон.
/// </summary>
[Column(Order = 3)]
[MaxLength(255)]
[Comment("Уровень заклинания")]
public string Key { get; set; } = null!;
/// <summary>
/// Применяется ли модификатор к урону заклинания.
/// </summary>
[Column(Order = 4)]
[Comment("Применяется модификатор?")]
public bool? ApplyMod { get; set; }
/// <summary>
/// Категория урона заклинания, если применимо.
/// </summary>
[Column(Order = 5)]
[Comment("Категория урона заклинания")]
public SpellDamageCategory? Category { get; set; }
/// <summary>
/// Формула урона заклинания.
/// </summary>
[Column(Order = 6)]
[MaxLength(255)]
[Comment("Формула урона заклинания")]
public string Formula { get; set; } = null!;
/// <summary>
/// Тип урона заклинания.
/// </summary>
[Column(Order = 7)]
[Comment("Тип урона заклинания")]
public SpellDamageKinds Kinds { get; set; }
/// <summary>
/// Материалы, необходимые для заклинания, если применимо.
/// </summary>
[Column(Order = 8)]
[MaxLength(255)]
[Comment("Материалы для заклинания")]
public string? Materials { get; set; }
/// <summary>
/// Тип урона заклинания, если применимо (например, "fire", "cold" и т.д.).
/// </summary>
[Column(Order = 9)]
[MaxLength(255)]
[Comment("Тип урона заклинания")]
public string Type { get; set; } = null!;
/// <summary>
/// Заклинание, к которому относится урон.
/// </summary>
public virtual Spell Spell { get; set; } = null!;
}
@@ -0,0 +1,52 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
namespace Pf2e.Database.Model.Structure.SpellRelation;
/// <summary>
/// Защита от заклинания.
/// </summary>
public class SpellDefense
{
/// <summary>
/// Идентификатор защиты от заклинания.
/// </summary>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Column(Order = 1)]
[Comment("Идентификатор")]
public int Id { get; set; }
/// <summary>
/// Идентификатор заклинания, к которому относится защита.
/// </summary>
[Column(Order = 2)]
[Comment("Идентификатор заклинания")]
public int SpellId { get; set; }
/// <summary>
/// Флаг, указывающий, является ли защита от заклинания пассивной (например, для заклинаний, которые не требуют активного действия для защиты).
/// </summary>
[Column(Order = 3)]
public bool IsPassive { get; set; }
/// <summary>
/// Флаг, указывающий, является ли защита базовой.
/// </summary>
[Column(Order = 4)]
[Comment("Базовый спасборосок?")]
public bool? IsBasic { get; set; }
/// <summary>
/// Статистика, используемая для защиты от заклинания (например, "Fortitude", "Reflex", "Will").
/// </summary>
[Column(Order = 5)]
[MaxLength(255)]
public string Statistic { get; set; } = null!;
/// <summary>
/// Заклинание, к которому относится защита.
/// </summary>
public virtual Spell Spell { get; set; } = null!;
}
+61
View File
@@ -0,0 +1,61 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
using Pf2e.Database.Model.Base;
using Pf2e.Database.Model.Common.I18n;
using Pf2e.Database.Model.Enum;
using Pf2e.Database.Model.Structure.ClassRelation;
namespace Pf2e.Database.Model.Structure;
/// <summary>
/// Подкласс.
/// </summary>
[EntityOfType(EntityType.Subclass)]
public class Subclass : IEntity
{
/// <inheritdoc/>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[Column(Order = 1)]
[Comment("Идентификатор")]
public int Id { get; set; }
/// <inheritdoc/>
public Entity Entity { get; set; } = null!;
/// <summary>
/// Идетификатор группы подклассов.
/// </summary>
[Column(Order = 2)]
[Comment("Группа подклассов")]
public int GroupId { get; set; }
/// <summary>
/// Группа подклассов.
/// </summary>
public SubclassGroup Group { get; set; } = null!;
/// <summary>
/// Дополнительная информация.
/// </summary>
[Column(Order = 3)]
[Comment("Дополнительная информация")]
public LocaleString Additional { get; set; } = [];
/// <summary>
/// Дополнительная информация в формате JSON.
/// </summary>
[Column(Order = 4)]
public string Extra { get; set; } = "{}";
/// <summary>
/// Коллекция сущностей, связанных с подклассом.
/// </summary>
public ICollection<Entity> Entities { get; set; } = [];
/// <summary>
/// Коллекция тренировок, связанных с подклассом.
/// </summary>
public ICollection<SubclassTrainings> Trainings { get; set; } = [];
}
@@ -0,0 +1,41 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
using Pf2e.Database.Model.Base;
using Pf2e.Database.Model.Enum;
namespace Pf2e.Database.Model.Structure;
/// <summary>
/// Группа подклассов.
/// </summary>
[EntityOfType(EntityType.SubclassGroup)]
public class SubclassGroup : IEntity
{
/// <inheritdoc/>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[Column(Order = 1)]
[Comment("Идентификатор")]
public int Id { get; set; }
/// <inheritdoc/>
public Entity Entity { get; set; } = null!;
/// <summary>
/// Идентификатор класса.
/// </summary>
[Column(Order = 2)]
[Comment("Идентификатор класса")]
public int? ClassId { get; set; } = null;
/// <summary>
/// Класс, к которому относится группа подклассов.
/// </summary>
public Class? Class { get; set; } = null!;
/// <summary>
/// Коллекция подклассов, относящихся к этой группе.
/// </summary>
public ICollection<Subclass> Subclasses { get; set; } = [];
}
+36
View File
@@ -0,0 +1,36 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
using Pf2e.Database.Model.Base;
using Pf2e.Database.Model.Enum;
namespace Pf2e.Database.Model.Structure;
/// <summary>
/// Признак.
/// </summary>
[EntityOfType(EntityType.Trait)]
public class Trait : IEntity
{
/// <inheritdoc/>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[Column(Order = 1)]
[Comment("Идентификатор")]
public int Id { get; set; }
/// <inheritdoc/>
public Entity Entity { get; set; } = null!;
/// <summary>
/// Тип признака.
/// </summary>
[Column(Order = 2)]
[Comment("Тип признака")]
public TraitType Type { get; set; }
/// <summary>
/// Сущности, которые имеют данный признак.
/// </summary>
public ICollection<Entity> Entities { get; set; } = [];
}
+46
View File
@@ -0,0 +1,46 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<GenerateRuntimeConfigurationFiles>True</GenerateRuntimeConfigurationFiles>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<DefaultDocumentationFolder>..\..\Documentation\$(MSBuildProjectName)</DefaultDocumentationFolder>
<DefaultDocumentationFileNameFactory>Name</DefaultDocumentationFileNameFactory>
</PropertyGroup>
<ItemGroup>
<None Include="..\Scrapper\.env" Link=".env">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="DefaultDocumentation" Version="1.0.2" PrivateAssets="all" />
<PackageReference Include="dotenv.net" Version="3.2.1" />
<PackageReference Include="EFCore.NamingConventions" Version="9.0.0" />
<PackageReference Include="linq2db" Version="5.4.1" />
<PackageReference Include="linq2db.EntityFrameworkCore" Version="9.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.4">
<!--<PrivateAssets>all</PrivateAssets>-->
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.4" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Npgsql" Version="9.0.3" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
</ItemGroup>
<ItemGroup>
<None Update="RebuildDatabase.bat">
<Generator></Generator>
</None>
</ItemGroup>
<ItemGroup>
<Service Include="{508349b6-6b84-4df5-91f0-309beebad82d}" />
</ItemGroup>
<ItemGroup>
<Compile Update="RebuildDatabase.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>RebuildDatabase.bat</DependentUpon>
</Compile>
</ItemGroup>
</Project>
+187
View File
@@ -0,0 +1,187 @@
using dotenv.net;
using dotenv.net.Utilities;
using Microsoft.EntityFrameworkCore;
using Pf2e.Database.Model.Base;
using Pf2e.Database.Model.Base.Keywords;
using Pf2e.Database.Model.Base.Source;
using Pf2e.Database.Model.Base.Targets;
using Pf2e.Database.Model.Common;
using Pf2e.Database.Model.Common.I18n;
using Pf2e.Database.Model.Enum;
using Pf2e.Database.Model.Structure;
using Pf2e.Database.Model.Structure.AncestryRelation;
using Pf2e.Database.Model.Structure.BackgroundRelation;
using Pf2e.Database.Model.Structure.ClassRelation;
using Pf2e.Database.Model.Structure.DeityRelation;
using Pf2e.Database.Model.Structure.FeatRelation;
using Pf2e.Database.Model.Structure.RitualRelation;
using Pf2e.Database.Model.Structure.SpellRelation;
namespace Pf2e.Database;
/// <summary>
/// Контекст базы данных.
///
/// Разделён на 2 файла для удобства навигации и чтения кода.
///
/// - в `Pf2eDbContext.cs` находится набор таблиц и конфигурация БД,
/// - в `Pf2eDbContextModel.cs` находится Fluent API описание связей в модели.
/// </summary>
// ReSharper disable once InconsistentNaming
public partial class Pf2eDbContext : DbContext
{
/// <summary>
/// Инициализирует новый экземпляр класса <see cref="Pf2eDbContext"/> без параметров.
/// </summary>
public Pf2eDbContext() { }
/// <summary>
/// Инициализирует новый экземпляр класса <see cref="Pf2eDbContext"/> с указанными параметрами конфигурации базы данных.
/// </summary>
/// <param name="options">Конфигурация базы данных.</param>
public Pf2eDbContext(DbContextOptions<Pf2eDbContext> options)
: base(options) { }
#region DbSets
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
public virtual DbSet<Model.Structure.Action> Actions { get; set; }
public virtual DbSet<Ancestry> Ancestries { get; set; }
public virtual DbSet<Archetype> Archetypes { get; set; }
public virtual DbSet<AncestryBoost> AncestryBoosts { get; set; }
public virtual DbSet<AncestryItem> AncestryItems { get; set; }
public virtual DbSet<AncestryLanguage> AncestryLanguages { get; set; }
public virtual DbSet<Background> Backgrounds { get; set; }
public virtual DbSet<BackgroundBoost> BackgroundBoosts { get; set; }
public virtual DbSet<BackgroundItem> BackgroundItems { get; set; }
public virtual DbSet<BackgroundTraining> BackgroundSkills { get; set; }
public virtual DbSet<Class> Classes { get; set; }
public virtual DbSet<ClassKeyAttribute> ClassKeyAbilities { get; set; }
public virtual DbSet<ClassProgression> ClassProgressions { get; set; }
public virtual DbSet<ClassTraining> ClassTrainings { get; set; }
public virtual DbSet<SubclassGroup> SubclassGroups { get; set; }
public virtual DbSet<Subclass> Subclasses { get; set; }
public virtual DbSet<Condition> Conditions { get; set; }
public virtual DbSet<Deity> Deities { get; set; }
public virtual DbSet<DeityAttribute> DeityAttributes { get; set; }
public virtual DbSet<DeityDomain> DeityDomains { get; set; }
public virtual DbSet<DeitySpell> DeitySpells { get; set; }
public virtual DbSet<DeityWeapon> DeityWeapons { get; set; }
public virtual DbSet<Domain> Domains { get; set; }
public virtual DbSet<Entity> Entities { get; set; }
public virtual DbSet<EntityAlias> EntityAliases { get; set; }
public virtual DbSet<EntityExternal> EntityExternals { get; set; }
public virtual DbSet<EntitySource> EntitySources { get; set; }
public virtual DbSet<ExternalTarget> ExternalTargets { get; set; }
public virtual DbSet<Feat> Feats { get; set; }
public virtual DbSet<FeatPrerequisite> FeatPrerequisites { get; set; }
public virtual DbSet<Frequency> Frequencies { get; set; }
public virtual DbSet<Heritage> Heritages { get; set; }
public virtual DbSet<Keyword> Keywords { get; set; }
public virtual DbSet<KeywordAlias> KeywordAliases { get; set; }
public virtual DbSet<Language> Languages { get; set; }
public virtual DbSet<LegacyRemaster> LegacyRemasters { get; set; }
public virtual DbSet<Ritual> Rituals { get; set; }
public virtual DbSet<RitualArea> RitualAreas { get; set; }
public virtual DbSet<RitualCheck> RitualChecks { get; set; }
public virtual DbSet<Rule> Rules { get; set; }
public virtual DbSet<Source> Sources { get; set; }
public virtual DbSet<Spell> Spells { get; set; }
public virtual DbSet<SpellArea> SpellAreas { get; set; }
public virtual DbSet<SpellDamage> SpellDamages { get; set; }
public virtual DbSet<SpellDefense> SpellDefenses { get; set; }
public virtual DbSet<Training> Trainings { get; set; }
public virtual DbSet<DeityCategory> DeityCategories { get; set; }
public virtual DbSet<Trait> Traits { get; set; }
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
#endregion
/// <inheritdoc/>
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
DotEnv.Load(options: new DotEnvOptions(probeForEnv: true, probeLevelsToSearch: 2));
string databaseConnection = EnvReader.GetStringValue("DATABASE_CONNECTION");
// optionsBuilder.LogTo(Console.WriteLine);
optionsBuilder.UseSnakeCaseNamingConvention();
optionsBuilder.EnableSensitiveDataLogging();
optionsBuilder.UseNpgsql(
databaseConnection,
builder =>
{
builder
.MapEnum<LocaleType>()
.MapEnum<ActionCategory>()
.MapEnum<ActionEnum>()
.MapEnum<AttributeValues>()
.MapEnum<ClassProgressionType>()
.MapEnum<ConditionGroup>()
.MapEnum<DeityType>()
.MapEnum<DeityFonts>()
.MapEnum<DeitySanctification>()
.MapEnum<ElementEnum>()
.MapEnum<EntityRarity>()
.MapEnum<EntityType>()
.MapEnum<FeatCategory>()
.MapEnum<SizeEnum>()
.MapEnum<SourceCategory>()
.MapEnum<SpellAreaType>()
.MapEnum<SpellDamageCategory>()
.MapEnum<SpellDamageKinds>()
.MapEnum<SpellTradition>()
.MapEnum<TrainingRank>()
.MapEnum<TraitType>()
.MapEnum<VisionEnum>();
}
);
}
}
+656
View File
@@ -0,0 +1,656 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq.Expressions;
using System.Reflection;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Pf2e.Database.Model;
using Pf2e.Database.Model.Base;
using Pf2e.Database.Model.Base.Keywords;
using Pf2e.Database.Model.Base.Source;
using Pf2e.Database.Model.Base.Targets;
using Pf2e.Database.Model.Common;
using Pf2e.Database.Model.Common.I18n;
using Pf2e.Database.Model.Structure;
using Pf2e.Database.Model.Structure.AncestryRelation;
using Pf2e.Database.Model.Structure.ArchetypeRelation;
using Pf2e.Database.Model.Structure.BackgroundRelation;
using Pf2e.Database.Model.Structure.ClassRelation;
using Pf2e.Database.Model.Structure.DeityRelation;
using Pf2e.Database.Model.Structure.FeatRelation;
using Pf2e.Database.Model.Structure.RitualRelation;
using Pf2e.Database.Model.Structure.SpellRelation;
namespace Pf2e.Database;
/// <inheritdoc/>
[SuppressMessage("ReSharper", "InconsistentNaming")]
public partial class Pf2eDbContext
{
/// <summary>
/// Длина названия сущности.
/// </summary>
public const int NameLength = 255;
/// <inheritdoc/>
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.UseIdentityByDefaultColumns();
// DefineEnums(modelBuilder);
DefineBase(modelBuilder);
DefineCommon(modelBuilder);
DefineStructure(modelBuilder);
DefineMethods(modelBuilder);
}
/// <summary>
/// Устанавливает связь между сущностью и её корневой сущностью.
/// </summary>
/// <typeparam name="T">Тип сущности.</typeparam>
/// <param name="entity">Генератор настроек таблицы сущности.</param>
private void BindEntity<T>(EntityTypeBuilder<T> entity)
where T : class, IEntity
{
entity
.HasOne(e => e.Entity)
.WithOne()
.HasForeignKey<T>(e => e.Id)
.IsRequired()
.OnDelete(DeleteBehavior.Cascade);
}
/// <summary>
/// Устанавливает связь между сущностью и локализованной строкой.
/// </summary>
/// <typeparam name="T">Тип сущности.</typeparam>
/// <param name="entity">Генератор настроек таблицы сущности.</param>
/// <param name="func">Функция, выбирающая поле сущности.</param>
/// <param name="required">Флаг, указывающий, что локализованная строка не может быть `null`.</param>
private void BindLocaleString<T>(
EntityTypeBuilder<T> entity,
Expression<Func<T, LocaleString?>> func,
bool required = true
)
where T : class
{
entity
.Property(func)
.HasConversion(
v => v == null ? null : v.Serialize(),
v => v == null ? null : LocaleString.Deserialize(v)
)
.HasColumnType("jsonb")
.IsRequired(required);
}
/// <summary>
/// Определяет базовые сущности и их связи.
/// </summary>
/// <param name="modelBuilder">Построитель модели.</param>
private void DefineBase(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Entity>(entity =>
{
BindLocaleString(entity, e => e.Name);
entity.HasMany(e => e.Traits).WithMany(t => t.Entities);
entity.HasMany(e => e.Keywords).WithMany(t => t.Entities);
// EntityAliases
entity
.HasMany(e => e.Aliases)
.WithOne(a => a.Entity)
.HasForeignKey(a => a.EntityId)
.OnDelete(DeleteBehavior.Cascade);
// EntitySource
entity
.HasMany(e => e.Sources)
.WithOne(e => e.Entity)
.HasForeignKey(e => e.EntityId)
.OnDelete(DeleteBehavior.Cascade);
// EntityExternal
entity
.HasMany(e => e.Externals)
.WithOne(e => e.Entity)
.HasForeignKey(e => e.EntityId)
.OnDelete(DeleteBehavior.Cascade);
// LegacyRemaster
entity
.HasMany(e => e.LegacyEntities)
.WithMany(e => e.RemasterEntities)
.UsingEntity<LegacyRemaster>(
l =>
l.HasOne(lr => lr.LegacyEntity)
.WithMany(e => e.LegacyTransferEntities)
.HasForeignKey(lr => lr.LegacyId),
r =>
r.HasOne(lr => lr.RemasterEntity)
.WithMany(e => e.RemasterTransferEntities)
.HasForeignKey(lr => lr.RemasterId)
);
});
// Source
modelBuilder.Entity<Source>(entity =>
{
entity.Property(e => e.Id).HasColumnName("id");
entity
.HasMany(s => s.EntitySources)
.WithOne(e => e.Source)
.HasForeignKey(e => e.SourceId)
.OnDelete(DeleteBehavior.Cascade);
// + EntitySource
});
modelBuilder.Entity<EntitySource>(entity => { });
// External
modelBuilder.Entity<ExternalTarget>(entity =>
{
entity
.HasMany(t => t.EntityExternals)
.WithOne(e => e.Target)
.HasForeignKey(e => e.TargetId)
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity<EntityExternal>(entity => { });
// Keywords
modelBuilder.Entity<Keyword>(entity =>
{
BindLocaleString(entity, k => k.Name);
entity
.HasMany(e => e.Aliases)
.WithOne(e => e.Keyword)
.HasForeignKey(e => e.KeywordId)
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity<KeywordAlias>(entity => { });
}
/// <summary>
/// Определяет общие сущности и их связи.
/// </summary>
/// <param name="modelBuilder">Построитель модели.</param>
private void DefineCommon(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Frequency>(entity => { });
modelBuilder.Entity<Language>(entity => { });
modelBuilder.Entity<Training>(entity =>
{
BindLocaleString(entity, t => t.Name);
});
}
/// <summary>
/// Определяет структуру сущностей и их связи.
/// </summary>
/// <param name="modelBuilder">Построитель модели.</param>
private void DefineStructure(ModelBuilder modelBuilder)
{
// Action
modelBuilder.Entity<Model.Structure.Action>(entity =>
{
BindEntity(entity);
});
// Ancestry
modelBuilder.Entity<Ancestry>(entity =>
{
BindEntity(entity);
entity
.HasMany(e => e.AncestryBoosts)
.WithOne(e => e.Ancestry)
.HasForeignKey(e => e.AncestryId)
.OnDelete(DeleteBehavior.Cascade);
entity
.HasMany(e => e.AncestryItems)
.WithOne(e => e.Ancestry)
.HasForeignKey(e => e.AncestryId)
.OnDelete(DeleteBehavior.Cascade);
entity
.HasMany(e => e.AncestryLanguages)
.WithOne(e => e.Ancestry)
.HasForeignKey(e => e.AncestryId)
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity<AncestryBoost>(entity => { });
modelBuilder.Entity<AncestryItem>(entity =>
{
entity
.HasOne(d => d.Item)
.WithMany()
.HasForeignKey(d => d.ItemId)
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity<AncestryLanguage>(entity =>
{
entity
.HasOne(d => d.Language)
.WithMany()
.HasForeignKey(d => d.LanguageId)
.OnDelete(DeleteBehavior.Cascade);
});
// Archetype
modelBuilder.Entity<Archetype>(entity =>
{
BindEntity(entity);
entity
.HasOne(a => a.DedicationFeat)
.WithOne()
.HasForeignKey<Archetype>(a => a.DedicationFeatId)
.OnDelete(DeleteBehavior.Cascade);
entity
.HasMany(a => a.Feats)
.WithMany(f => f.Archetypes)
.UsingEntity<ArchetypeFeats>(
l => l.HasOne<Feat>().WithMany().HasForeignKey(af => af.FeatId),
r => r.HasOne<Archetype>().WithMany().HasForeignKey(af => af.ArchetypeId)
);
});
// Background
modelBuilder.Entity<Background>(entity =>
{
BindEntity(entity);
});
modelBuilder.Entity<BackgroundBoost>(entity =>
{
entity
.HasOne(d => d.Background)
.WithMany(p => p.BackgroundBoosts)
.HasForeignKey(d => d.BackgroundId)
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity<BackgroundItem>(entity =>
{
entity
.HasOne(d => d.Background)
.WithMany(p => p.BackgroundItems)
.HasForeignKey(d => d.BackgroundId)
.OnDelete(DeleteBehavior.Cascade);
entity
.HasOne(d => d.Entity)
.WithMany()
.HasForeignKey(d => d.EntityId)
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity<BackgroundTraining>(entity =>
{
entity
.HasOne(d => d.Background)
.WithMany(p => p.BackgroundSkills)
.HasForeignKey(d => d.BackgroundId)
.OnDelete(DeleteBehavior.Cascade);
});
// Class
modelBuilder.Entity<Class>(entity =>
{
BindEntity(entity);
});
modelBuilder.Entity<ClassKeyAttribute>(entity =>
{
entity
.HasOne(d => d.Class)
.WithMany(p => p.ClassKeyAbilities)
.HasForeignKey(d => d.ClassId)
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity<ClassProgression>(entity =>
{
entity
.HasOne(d => d.Class)
.WithMany(p => p.ClassProgressions)
.HasForeignKey(d => d.ClassId)
.OnDelete(DeleteBehavior.Cascade);
entity
.HasOne(d => d.Entity)
.WithMany()
.HasForeignKey(d => d.EntityId)
.OnDelete(DeleteBehavior.SetNull);
});
modelBuilder.Entity<ClassTraining>(entity =>
{
entity
.HasOne(d => d.Class)
.WithMany(p => p.ClassTrainings)
.HasForeignKey(d => d.ClassId)
.OnDelete(DeleteBehavior.Cascade);
entity
.HasOne(d => d.Training)
.WithMany()
.HasForeignKey(d => d.TrainingId)
.OnDelete(DeleteBehavior.SetNull);
});
// Subclass
modelBuilder.Entity<SubclassGroup>(entity =>
{
BindEntity(entity);
entity
.HasOne(g => g.Class)
.WithMany(c => c.SubclassGroups)
.HasForeignKey(g => g.ClassId)
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity<Subclass>(entity =>
{
BindEntity(entity);
BindLocaleString(entity, s => s.Additional);
entity
.HasOne(s => s.Group)
.WithMany(g => g.Subclasses)
.HasForeignKey(s => s.GroupId)
.OnDelete(DeleteBehavior.Cascade);
entity
.HasMany(e => e.Entities)
.WithMany()
.UsingEntity<SubclassProvidedEntities>(
l => l.HasOne<Entity>().WithMany().HasForeignKey(se => se.EntityId),
r => r.HasOne<Subclass>().WithMany().HasForeignKey(se => se.SubclassId)
);
entity
.HasMany(e => e.Trainings)
.WithOne(t => t.Subclass)
.HasForeignKey(t => t.SubclassId)
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity<SubclassTrainings>(entity =>
{
entity
.HasOne(d => d.Subclass)
.WithMany(p => p.Trainings)
.HasForeignKey(d => d.SubclassId)
.OnDelete(DeleteBehavior.Cascade);
});
// Condition
modelBuilder.Entity<Condition>(entity =>
{
BindEntity(entity);
});
// Domain
modelBuilder.Entity<Domain>(entity =>
{
BindEntity(entity);
entity
.HasOne(d => d.DomainSpell)
.WithMany()
.HasForeignKey(d => d.DomainSpellId)
.OnDelete(DeleteBehavior.Cascade);
entity
.HasOne(d => d.AdvancedDomainSpell)
.WithMany()
.HasForeignKey(d => d.AdvancedDomainSpellId)
.OnDelete(DeleteBehavior.Cascade);
entity
.HasOne(d => d.ApocryphalSpell)
.WithMany()
.HasForeignKey(d => d.ApocryphalSpellId)
.OnDelete(DeleteBehavior.SetNull);
entity
.HasOne(d => d.AdvancedApocryphalSpell)
.WithMany()
.HasForeignKey(d => d.AdvancedApocryphalSpellId)
.OnDelete(DeleteBehavior.SetNull);
entity
.HasMany(d => d.DeityDomains)
.WithOne(dd => dd.Domain)
.HasForeignKey(dd => dd.DomainId)
.OnDelete(DeleteBehavior.Cascade);
});
// Deity
modelBuilder.Entity<Deity>(entity =>
{
BindEntity(entity);
BindLocaleString(entity, d => d.Anathema);
BindLocaleString(entity, d => d.Edicts);
BindLocaleString(entity, d => d.ReligiousSymbol);
BindLocaleString(entity, d => d.SacredAnimal);
BindLocaleString(entity, d => d.AreaOfConcern);
entity
.HasMany(e => e.DeityAttributes)
.WithOne(e => e.Deity)
.HasForeignKey(e => e.DeityId)
.OnDelete(DeleteBehavior.Cascade);
entity
.HasMany(e => e.DeityDomains)
.WithOne(e => e.Deity)
.HasForeignKey(e => e.DeityId)
.OnDelete(DeleteBehavior.Cascade);
entity
.HasMany(e => e.DeitySpells)
.WithOne(e => e.Deity)
.HasForeignKey(e => e.DeityId)
.OnDelete(DeleteBehavior.Cascade);
entity
.HasMany(e => e.DeityWeapons)
.WithOne(e => e.Deity)
.HasForeignKey(e => e.DeityId)
.OnDelete(DeleteBehavior.Cascade);
entity.HasMany(e => e.Skills).WithMany();
});
modelBuilder.Entity<DeityAttribute>(entity => { });
modelBuilder.Entity<DeityDomain>(entity => { });
modelBuilder.Entity<DeitySpell>(entity =>
{
entity
.HasOne(d => d.Spell)
.WithMany()
.HasForeignKey(d => d.SpellId)
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity<DeityWeapon>(entity => { });
// Feat
modelBuilder.Entity<Feat>(entity =>
{
BindEntity(entity);
entity
.HasMany(e => e.FeatPrerequisites)
.WithOne(e => e.Feat)
.HasForeignKey(e => e.FeatId)
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity<FeatPrerequisite>(entity => { });
// Heritage
modelBuilder.Entity<Heritage>(entity =>
{
BindEntity(entity);
entity
.HasOne(d => d.Ancestry)
.WithMany(p => p.Heritages)
.HasForeignKey(d => d.AncestryId)
.OnDelete(DeleteBehavior.Cascade);
});
// Spell
modelBuilder.Entity<Spell>(entity =>
{
BindEntity(entity);
BindLocaleString(entity, s => s.Trigger, false);
BindLocaleString(entity, s => s.Target, false);
BindLocaleString(entity, s => s.Cost, false);
BindLocaleString(entity, s => s.Requirement, false);
entity
.HasMany(e => e.SpellAreas)
.WithOne(e => e.Spell)
.HasForeignKey(e => e.SpellId)
.OnDelete(DeleteBehavior.Cascade);
entity
.HasMany(e => e.SpellDefenses)
.WithOne(e => e.Spell)
.HasForeignKey(e => e.SpellId)
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity<SpellArea>(entity =>
{
BindLocaleString(entity, a => a.Details, false);
});
modelBuilder.Entity<SpellDefense>(entity => { });
// Ritual
modelBuilder.Entity<Ritual>(entity =>
{
BindEntity(entity);
BindLocaleString(entity, s => s.RangeString, false);
BindLocaleString(entity, s => s.Target, false);
BindLocaleString(entity, s => s.Cost, false);
BindLocaleString(entity, s => s.Requirement, false);
BindLocaleString(entity, s => s.PrimaryCheckSuffix, false);
BindLocaleString(entity, s => s.SecondaryCastersSuffix, false);
entity
.HasOne(r => r.Area)
.WithOne(a => a.Ritual)
.HasForeignKey<RitualArea>(a => a.RitualId)
.OnDelete(DeleteBehavior.Cascade);
entity
.HasMany(r => r.Checks)
.WithOne(c => c.Ritual)
.HasForeignKey(c => c.RitualId)
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity<RitualArea>(entity =>
{
BindLocaleString(entity, a => a.Details, false);
});
modelBuilder.Entity<RitualCheck>(entity =>
{
BindLocaleString(entity, a => a.TrainingString, false);
entity
.HasOne(c => c.Training)
.WithMany()
.HasForeignKey(c => c.TrainingId)
.OnDelete(DeleteBehavior.SetNull);
});
// DeityCategory
modelBuilder.Entity<DeityCategory>(entity =>
{
BindEntity(entity);
BindLocaleString(entity, d => d.Description);
});
// Trait
modelBuilder.Entity<Trait>(entity =>
{
BindEntity(entity);
});
// Rule
modelBuilder.Entity<Rule>(entity =>
{
BindEntity(entity);
entity
.HasOne(r => r.Parent)
.WithMany(r => r.Children)
.HasForeignKey(r => r.ParentId)
.IsRequired(false);
});
}
/// <summary>
/// Определяет методы, которые будут использоваться в запросах LINQ.
/// </summary>
/// <param name="modelBuilder">Построитель модели.</param>
private void DefineMethods(ModelBuilder modelBuilder)
{
MethodInfo localeSearch = typeof(Pf2eDbContext).GetMethod(
nameof(LocaleSearch),
BindingFlags.Static | BindingFlags.Public,
[typeof(LocaleString), typeof(string)]
)!;
modelBuilder.HasDbFunction(
localeSearch,
builder =>
{
builder.HasName("locale_search");
builder.HasParameter("data").HasStoreType("jsonb");
builder.HasParameter("search_value");
}
);
MethodInfo localeSearchArray = typeof(Pf2eDbContext).GetMethod(
nameof(LocaleSearchArray),
BindingFlags.Static | BindingFlags.Public,
[typeof(LocaleString), typeof(IEnumerable<string>)]
)!;
modelBuilder.HasDbFunction(
localeSearchArray,
builder =>
{
builder.HasName("locale_search_array");
builder.HasParameter("data").HasStoreType("jsonb");
builder.HasParameter("search_values");
}
);
}
/// <summary>
/// CLR-представление функции поиска в локализованной строке.
/// </summary>
/// <param name="data">Локализованная строка, в которой будет осуществляться поиск.</param>
/// <param name="search_value">Искомое значение для поиска.</param>
/// <returns>Флаг, указывающий, найдено ли искомое значение в локализованной строке.</returns>
// ReSharper disable once InconsistentNaming
public static bool LocaleSearch(LocaleString data, string search_value) =>
throw new InvalidOperationException("Only for LINQ translation");
/// <summary>
/// CLR-представление функции поиска в локализованной строке с массивом значений для поиска.
/// </summary>
/// <param name="data">Локализованная строка, в которой будет осуществляться поиск.</param>
/// <param name="search_values">Массив искомых значений для поиска.</param>
/// <returns>Флаг, указывающий, найдено ли хотя бы одно искомое значение в локализованной строке.</returns>
// ReSharper disable once InconsistentNaming
public static bool LocaleSearchArray(LocaleString data, IEnumerable<string> search_values) =>
throw new InvalidOperationException("Only for LINQ translation");
}
+10
View File
@@ -0,0 +1,10 @@
@echo off
del /s .\Migrations\Pf2eDbContextModelSnapshot.cs
del /s .\Migrations\*_1.cs
del /s .\Migrations\*_1.Designer.cs
dotnet ef database drop -f
dotnet ef migrations add 1
dotnet ef database update
+11
View File
@@ -0,0 +1,11 @@
# Путь к папке для распаковки репозиториев обрабатываемых данных
DATA_DIR=D:\path\to\directory\with\repositories
# Путь к месту сохранения схем
OUTPUT_DIR=D:\path\to\output\directory
# Строка подключения к БД
DATABASE_CONNECTION=Host=localhost;Port=5433;Database=usersdb2;Username=postgres;Password=password
# Максимальное количество потоков.
MAX_THREADS=60
+84
View File
@@ -0,0 +1,84 @@
using System;
using System.ComponentModel;
using System.IO;
using dotenv.net;
using dotenv.net.Utilities;
namespace Pf2e.Scrapper;
/// <summary>
/// Настройки программы.
/// </summary>
internal class ApplicationSettings
{
/// <summary>
/// Конструктор класса <see cref="ApplicationSettings"/>.
/// </summary>
public ApplicationSettings()
{
DotEnv.Load();
DataDirPath = EnvReader.GetStringValue("DATA_DIR");
OutputDirPath = EnvReader.GetStringValue("OUTPUT_DIR");
DatabaseConnection = EnvReader.GetStringValue("DATABASE_CONNECTION");
MaxThreads = Convert.ToInt32(EnvReader.GetStringValue("MAX_THREADS"));
}
#region Настройки из .env
/// <summary>
/// Путь к папке с входными данными.
/// </summary>
[Browsable(true)]
[ReadOnly(true)]
[DisplayName("Путь к папке с входными данными.")]
[Description("Редактирование через .env-файл.")]
public string DataDirPath { get; }
/// <summary>
/// Папка в входными данными.
/// </summary>
[Browsable(false)]
public DirectoryInfo DataDir => new(DataDirPath);
/// <summary>
/// Путь к папке для сохранения результатов.
/// </summary>
[Browsable(true)]
[ReadOnly(true)]
[DisplayName("Путь к папке для сохранения результатов.")]
[Description("Редактирование через .env-файл.")]
public string OutputDirPath { get; }
/// <summary>
/// Папка для сохранения результатов.
/// </summary>
[Browsable(false)]
public string OutputDir => new(OutputDirPath);
/// <summary>
/// Строка подключения к базе данных.
/// </summary>
[Browsable(true)]
[ReadOnly(true)]
[DisplayName("Строка подключения к базе данных.")]
[Description("Редактирование через .env-файл.")]
public string DatabaseConnection { get; }
/// <summary>
/// Максимальное количество потоков.
/// </summary>
[Browsable(true)]
[ReadOnly(true)]
[DisplayName("Максимальное количество потоков.")]
[Description("Редактирование через .env-файл.")]
public int MaxThreads { get; }
#endregion
#region Основные настройки
//
#endregion
}
+255
View File
@@ -0,0 +1,255 @@
namespace Pf2e.Scrapper
{
/// <summary>
/// Модель классов ORM EntityFramework.
/// </summary>
internal static class NamespaceDoc { }
}
namespace Pf2e.Scrapper.Helpers
{
/// <summary>
/// Классы-помощники, используемые во всех частях проекта.
/// </summary>
internal static class NamespaceDoc { }
}
namespace Pf2e.Scrapper.Importers
{
/// <summary>
/// Описание импортёров, которые определяют наборы обработчиков данных и элементов, необходимых для их работы.
/// </summary>
internal static class NamespaceDoc { }
}
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd
{
/// <summary>
/// Импортёр данных из сайта <see href="https://aonprd.com/">Archives of Nethys Pathfinder 2e</see>.
/// </summary>
internal static class NamespaceDoc { }
}
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Compendium
{
/// <summary>
/// Компендиум данных из сайта <see href="https://aonprd.com/">Archives of Nethys Pathfinder 2e</see>.
/// </summary>
internal static class NamespaceDoc { }
}
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers
{
/// <summary>
/// Класс-помощники, которые используются в импортере данных из сайта <see href="https://aonprd.com/">Archives of Nethys Pathfinder 2e</see>.
/// </summary>
internal static class NamespaceDoc { }
}
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors
{
/// <summary>
/// Обработчики данных из сайта <see href="https://aonprd.com/">Archives of Nethys Pathfinder 2e</see>.
/// </summary>
internal static class NamespaceDoc { }
}
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Preparation
{
/// <summary>
/// Обработчики для подготовки данных <see href="https://aonprd.com/">Archives of Nethys Pathfinder 2e</see>.
/// </summary>
internal static class NamespaceDoc { }
}
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Subclasses
{
/// <summary>
/// Обработчики подклассов с сайта <see href="https://aonprd.com/">Archives of Nethys Pathfinder 2e</see>.
/// </summary>
internal static class NamespaceDoc { }
}
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Types
{
/// <summary>
/// Обработчики основных сущностей с сайта <see href="https://aonprd.com/">Archives of Nethys Pathfinder 2e</see>.
/// </summary>
internal static class NamespaceDoc { }
}
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Processors.Types.Rules
{
/// <summary>
/// Обработчики правил с сайта <see href="https://aonprd.com/">Archives of Nethys Pathfinder 2e</see>.
/// </summary>
internal static class NamespaceDoc { }
}
namespace Pf2e.Scrapper.Importers.Implementations.FoundryEn
{
/// <summary>
/// Импортёр данных из модуля <see href="https://github.com/foundryvtt/pf2e">pf2e</see> для FoundryVTT.
/// </summary>
internal static class NamespaceDoc { }
}
namespace Pf2e.Scrapper.Importers.Implementations.FoundryEn.Compendium
{
/// <summary>
/// Компендиум данных из модуля <see href="https://github.com/foundryvtt/pf2e">pf2e</see> для FoundryVTT.
/// </summary>
internal static class NamespaceDoc { }
}
namespace Pf2e.Scrapper.Importers.Implementations.FoundryEn.Processors.Preparation
{
/// <summary>
/// Обработчики для подготовки данных из модуля <see href="https://github.com/foundryvtt/pf2e">pf2e</see> для FoundryVTT.
/// </summary>
internal static class NamespaceDoc { }
}
namespace Pf2e.Scrapper.Importers.Repositories
{
/// <summary>
/// Общее описание репозиториев для хранения данных, которые используются в импортёрах и процессорах.
/// </summary>
internal static class NamespaceDoc { }
}
namespace Pf2e.Scrapper.Processors
{
/// <summary>
/// Общее описание обработчиков данных.
/// </summary>
internal static class NamespaceDoc { }
}
namespace Pf2e.Scrapper.Processors.Attributes
{
/// <summary>
/// Классы-аттрибуты, которые используются для описания обработчиков данных и их свойств.
/// </summary>
internal static class NamespaceDoc { }
}
namespace Pf2e.Scrapper.Processors.Collection
{
/// <summary>
/// Описание коллекции классов обработчиков данных.
/// </summary>
internal static class NamespaceDoc { }
}
namespace Pf2e.Scrapper.Processors.Definitions
{
/// <summary>
/// Общее описание классов и интерфейсов, которые используются в обработчиках данных.
/// </summary>
internal static class NamespaceDoc { }
}
namespace Pf2e.Scrapper.Processors.Factory
{
/// <summary>
/// Фабрики для создания экземпляров обработчиков данных на основе их атрибутов и конфигурации.
/// </summary>
internal static class NamespaceDoc { }
}
namespace Pf2e.Scrapper.SchemaBuilder
{
/// <summary>
/// Классы обработки вводимых JSON-данных и создание схемы данных для использовании при формировании обработчиков данных.
/// </summary>
internal static class NamespaceDoc { }
}
namespace Pf2e.Scrapper.SchemaBuilder.Types
{
/// <summary>
/// Типы полей, которые используются в схемах данных для описания структуры вводимых JSON-данных.
/// </summary>
internal static class NamespaceDoc { }
}
namespace Pf2e.Scrapper.WPF
{
/// <summary>
/// WPF-приложение для запуска обработчиков и просмотра данных Pathfinder 2e.
/// </summary>
internal static class NamespaceDoc { }
}
namespace Pf2e.Scrapper.WPF.AuthForm
{
/// <summary>
/// WPF-приложение для авторизации пользователя во внешнем сервисе.
/// </summary>
internal static class NamespaceDoc { }
}
namespace Pf2e.Scrapper.WPF.EntityViewer
{
/// <summary>
/// WPF-окно для просмотра сущностей Pathfinder 2e, с возможностью фильтрации и поиска по данным.
/// </summary>
internal static class NamespaceDoc { }
}
namespace Pf2e.Scrapper.WPF.EntityViewer.Component
{
/// <summary>
/// Компоненты, которые используются в WPF-окне просмотра сущностей Pathfinder 2e.
/// </summary>
internal static class NamespaceDoc { }
}
namespace Pf2e.Scrapper.WPF.EntityViewer.EntityViewStructure
{
/// <summary>
/// Структура представления сущностей Pathfinder 2e в WPF-приложении, включая классы и интерфейсы для отображения данных.
/// </summary>
internal static class NamespaceDoc { }
}
namespace Pf2e.Scrapper.WPF.EntityViewer.Stores
{
/// <summary>
/// Хранилища данных для WPF-приложения просмотра сущностей Pathfinder 2e, которые управляют состоянием и данными приложения.
/// </summary>
internal static class NamespaceDoc { }
}
namespace Pf2e.Scrapper.WPF.EntityViewer.ViewModel
{
/// <summary>
/// ViewModel для WPF-приложения просмотра сущностей Pathfinder 2e, которые связывают данные и представление.
/// </summary>
internal static class NamespaceDoc { }
}
namespace Pf2e.Scrapper.WPF.ProgressForm
{
/// <summary>
/// WPF-окно для отображения прогресса выполнения операций, таких как импорт данных или обработка сущностей Pathfinder 2e.
/// </summary>
internal static class NamespaceDoc { }
}
namespace Pf2e.Scrapper.WPF.StartForm
{
/// <summary>
/// WPF-окно для запуска приложения, которое позволяет пользователю выбрать импортёр и начать обработку данных Pathfinder 2e.
/// </summary>
internal static class NamespaceDoc { }
}
namespace Pf2e.Scrapper.WPF.StartForm.Components
{
/// <summary>
/// Компоненты, которые используются в WPF-окне запуска приложения для выбора импортёра и настройки параметров обработки данных Pathfinder 2e.
/// </summary>
internal static class NamespaceDoc { }
}
+44
View File
@@ -0,0 +1,44 @@
using System.IO;
using dotenv.net;
using dotenv.net.Utilities;
namespace Pf2e.Scrapper;
/// <summary>
/// Класс настроек.
/// </summary>
public static class EnvSettings
{
/// <summary>
/// Загрузка файла настроек `.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);
/// <summary>
/// Строка подключения к базе данных.
/// </summary>
public static string DatabaseConnection => EnvReader.GetStringValue("DATABASE_CONNECTION");
}
+56
View File
@@ -0,0 +1,56 @@
using System.Linq;
using System.Threading;
using Pf2e.Database;
using Slugify;
namespace Pf2e.Scrapper.Helpers;
/// <summary>
/// Класс предоставляет методы для работы с базой данных.
/// </summary>
public class DatabaseHelper
{
/// <summary>
/// Context для доступа к базе данных.
/// </summary>
public Pf2eDbContext Context { get; } = new();
/// <summary>
/// Сервис для работы с URL-Slug. Используется для преобразования названий в URL-friendly формат.
/// </summary>
public SlugHelper Slugger { get; }
/// <summary>
/// Последний найденный идентификатор сущности.
/// Используется для генерации новых ID сущностей.
/// </summary>
private int _id;
/// <summary>
/// Генерирует новый уникальный идентификатор сущности. и возвращает его.
/// </summary>
public int NextEntityId => ++_id;
/// <summary>
/// Блокировщик для генерации идентификаторов сущностей.
/// </summary>
public Lock IdLock { get; } = new();
/// <summary>
/// Конструктор класса <see cref="DatabaseHelper"/>
/// </summary>
/// <param name="slugHelper">[DI] Сервис для работы с URL-Slug</param>
public DatabaseHelper(SlugHelper slugHelper)
{
Slugger = slugHelper;
_id = Context.Entities.Any() ? Context.Entities.Max(e => e.Id) : 0;
}
/// <summary>
/// Генерирует slug из названия.
/// </summary>
public string GenerateSlug(string name)
{
return Slugger.GenerateSlug(name);
}
}
+181
View File
@@ -0,0 +1,181 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Pf2e.Database;
using Pf2e.Database.Model;
using Pf2e.Database.Model.Base;
using Pf2e.Database.Model.Enum;
using Pf2e.Database.Model.Structure;
namespace Pf2e.Scrapper.Helpers;
/// <summary>
/// Класс, обрабатывающий запросы к сущностям и подключающий необходимые данные.
/// </summary>
public class EntityView
{
/// <summary>
/// Корневая сущность.
/// </summary>
public Entity Entity { get; }
/// <summary>
/// Основная сущность.
/// </summary>
public IEntity? SubEntity { get; }
/// <summary>
/// Контекст базы данных, используемый для получения сущностей.
/// </summary>
private Pf2eDbContext Context { get; }
/// <summary>
/// Конструктор класса, который получает сущность из базы данных по типу и идентификатору и заполняет её данными.
/// </summary>
/// <param name="context">Контекст базы данных.</param>
/// <param name="type">Тип запрашиваемой сущности.</param>
/// <param name="id">Идентификатор запрашиваемой сущности.</param>
public EntityView(Pf2eDbContext context, EntityType type, int id)
{
Context = context;
Entity = GetEntity(type, id);
SubEntity = GetSubEntity(type, id);
}
/// <summary>
/// Конструктор класса, который получает сущность из базы данных по переданной сущности и заполняет её данными.
/// </summary>
/// <param name="context">Контекст базы данных.</param>
/// <param name="entity">Сущность, которую нужно обработать.</param>
public EntityView(Pf2eDbContext context, Entity entity)
{
Context = context;
Entity = entity;
SubEntity = GetSubEntity(entity.Type, entity.Id);
}
/// <summary>
/// Получает сущность из базы данных по типу и идентификатору.
/// </summary>
/// <param name="type">Тип сущности.</param>
/// <param name="id">Идентификатор сущности.</param>
/// <returns>Сущность, соответствующая указанному типу и идентификатору.</returns>
private Entity GetEntity(EntityType type, int id) =>
Context
.Entities.Where(e => e.Id == id && e.Type == type)
.Include(e => e.Sources)
.ThenInclude(s => s.Source)
.Include(e => e.Keywords)
.Include(e => e.Externals)
.ThenInclude(x => x.Target)
.Include(e => e.Traits)
.ThenInclude(t => t.Entity)
.FirstOrDefault() ?? throw new("Entity is null");
/// <summary>
/// Получает подчинённую сущность по типу и идентификатору.
/// </summary>
/// <param name="type">Тип сущности.</param>
/// <param name="id">Идентификатор сущности.</param>
/// <returns>Подчинённая сущность, соответствующая указанному типу и идентификатору.</returns>
private IEntity? GetSubEntity(EntityType type, int id)
{
Getter? getter = _getters.FirstOrDefault(g => g.Type == type);
return (getter != null)
? getter.Get(id, this.Context)
: (IEntity?)Context.Find(EntityOfTypeAttribute.Resolve(Entity), id);
}
/// <summary>
/// Словарь, содержащий методы получения сущностей из базы данных.
/// </summary>
private readonly ICollection<Getter> _getters =
[
new Getter<Ancestry>(
EntityType.Ancestry,
context => context.Ancestries.Include(a => a.AncestryBoosts)
),
new Getter<Archetype>(
EntityType.Archetype,
context =>
context
.Archetypes.Include(a => a.DedicationFeat)
.ThenInclude(f => f.Entity)
.Include(a => a.Feats)
.ThenInclude(f => f.Entity)
),
new Getter<Background>(
EntityType.Background,
context =>
context
.Backgrounds.Include(b => b.BackgroundBoosts)
.Include(b => b.BackgroundSkills)
.ThenInclude(t => t.Training)
.Include(b => b.BackgroundItems)
),
new Getter<Class>(
EntityType.Class,
context =>
context
.Classes.Include(c => c.ClassKeyAbilities)
.Include(c => c.ClassTrainings)
.ThenInclude(t => t.Training)
.Include(c => c.ClassProgressions)
.Include(c => c.SubclassGroups)
),
new Getter<Feat>(
EntityType.Feat,
context =>
context.Feats.Include(f => f.Archetypes).ThenInclude(a => a.Entity).AsQueryable()
),
new Getter<Spell>(
EntityType.Spell,
context => context.Spells.Include(s => s.SpellAreas).Include(s => s.SpellDefenses)
),
new Getter<Ritual>(
EntityType.Ritual,
context =>
context
.Rituals.Include(r => r.Area)
.Include(r => r.Checks)
.ThenInclude(c => c.Training)
.AsQueryable()
),
new Getter<Subclass>(
EntityType.Subclass,
context =>
context
.Subclasses.Include(s => s.Trainings)
.ThenInclude(t => t.Training)
.Include(s => s.Entities)
),
];
/// <summary>
/// Абстрактный класс, который определяет метод получения сущности из базы данных.
/// </summary>
/// <param name="type">Связанный тип сущности.</param>
private abstract class Getter(EntityType type)
{
public readonly EntityType Type = type;
public abstract IEntity? Get(int id, Pf2eDbContext context);
}
/// <summary>
/// Класс, определяющий метод получения сущности из базы данных.
/// </summary>
/// <typeparam name="T">Тип данных основной сущности.</typeparam>
/// <param name="type">Связанный тип сущности.</param>
/// <param name="method">Метод получения сущности из базы данных.</param>
private class Getter<T>(EntityType type, Func<Pf2eDbContext, IQueryable<T>> method)
: Getter(type)
where T : IEntity
{
public override IEntity? Get(int id, Pf2eDbContext context)
{
return method.Invoke(context).FirstOrDefault(c => c.Id == id);
}
}
}
+22
View File
@@ -0,0 +1,22 @@
namespace Pf2e.Scrapper.Helpers;
internal abstract class AbstractLogger
{
public abstract void Message(LogLevel level, string message);
public void Important(string message) => Message(LogLevel.Important, message);
public void Warning(string message) => Message(LogLevel.Warning, message);
public void Information(string message) => Message(LogLevel.Information, message);
public void Progress(string message) => Message(LogLevel.Progress, message);
}
internal enum LogLevel
{
Important,
Warning,
Information,
Progress,
}
+168
View File
@@ -0,0 +1,168 @@
using Pf2e.Scrapper.Processors.Definitions;
namespace Pf2e.Scrapper.Helpers;
internal class ProcessProgress : AbstractLogger
{
/// <summary>
/// Прогресс обработки обработчиков <see cref="IProcessor"/>.
/// </summary>
public ProgressItem Processors { get; } = new();
/// <summary>
/// Прогресс работы конкретного обработчика <see cref="IProcessor"/>.
/// </summary>
public ProgressItem Local { get; } = new();
/// <summary>
/// Конструктор класса <see cref="ProcessProgress"/>.
/// </summary>
public ProcessProgress()
{
Processors.Update += Update;
Local.Update += Update;
}
/// <summary>
/// Событие обновления
/// </summary>
private void Update()
{
ProgressUpdate?.Invoke(this);
}
public override void Message(LogLevel level, string message)
{
ProgressMessage?.Invoke(this, level, message);
}
public void Complete()
{
ProgressComplete?.Invoke(this);
}
public delegate void ProgressUpdateDelegate(ProcessProgress sender);
public event ProgressUpdateDelegate? ProgressUpdate;
public event ProgressUpdateDelegate? ProgressComplete;
public delegate void ProgressMessageDelegate(
ProcessProgress sender,
LogLevel level,
string message
);
public event ProgressMessageDelegate? ProgressMessage;
/// <summary>
/// Класс прогресса.
/// </summary>
public class ProgressItem
{
/// <summary>
/// Количество обрабатываемых процессов.
/// </summary>
public int Count
{
get => _count;
set
{
_count = value;
Update?.Invoke();
}
}
private int _count;
/// <summary>
/// Индекс текущего обрабатываемого процесса.
/// </summary>
public int Current
{
get => _current;
set
{
_current = value;
Update?.Invoke();
}
}
private int _current;
/// <summary>
/// Название текущего обрабатываемого процесса.
/// </summary>
public string Text
{
get => _text;
set
{
_text = value;
Update?.Invoke();
}
}
private string _text = "";
/// <summary>
/// Значение прогресса.
/// </summary>
public double Percent => Count > 0 ? 1.0 * Current / Count : 1.0;
/// <summary>
/// Сброс прогресса до нуля и установка нового максимального значения.
/// </summary>
/// <param name="max"></param>
public void Reset(int max = 0)
{
_count = max;
_current = 0;
_text = "";
Update?.Invoke();
}
/// <summary>
/// Отмечает начало работы над процессом, описываемым строкой <paramref name="text"/>.
/// </summary>
/// <param name="text">Строка, описывающая процесс.</param>
public void Start(string text)
{
_text = text;
Update?.Invoke();
}
/// <summary>
/// Отмечает конец работы над процессом и увеличивает значение прогресса.
/// </summary>
public void End()
{
_current++;
Update?.Invoke();
}
/// <summary>
/// Отмечает прогресс завершённым.
/// </summary>
public void Complete()
{
_current = _count;
_text = "Готово!";
Update?.Invoke();
}
/// <summary>
/// Получение строкового представления прогресса.
/// </summary>
/// <returns>Строковое представление прогресса.</returns>
public override string ToString()
{
return $"{Text} [{Current} / {Count}; {Percent:P}]";
}
/// <summary>
/// Делегат события обновления.
/// </summary>
public delegate void UpdateDelegate();
/// <summary>
/// Событие обновление.
/// </summary>
public event UpdateDelegate? Update;
}
}
+219
View File
@@ -0,0 +1,219 @@
using System;
using System.ComponentModel;
using System.Windows;
using Microsoft.Extensions.DependencyInjection;
using Pf2e.Scrapper.Helpers;
using Pf2e.Scrapper.Processors;
using Pf2e.Scrapper.Processors.Collection;
using Pf2e.Scrapper.WPF.EntityViewer;
using Pf2e.Scrapper.WPF.ProgressForm;
using Pf2e.Scrapper.WPF.StartForm;
using Pf2e.Database;
using Slugify;
namespace Pf2e.Scrapper;
internal sealed class ImportApplication : Application
{
public ImportApplication(
IServiceProvider serviceProvider,
ApplicationSettings settings,
ProcessorCollection processors
)
{
ServiceProvider = serviceProvider;
Settings = settings;
Processors = processors;
CurrentStage = ApplicationStage.InitialSettings;
//CurrentStage = ApplicationStage.Viewer;
}
#region "Поля"
/// <summary>
/// Текущий этап выполнения программы.
/// </summary>
public ApplicationStage CurrentStage
{
get => _currentStage;
set
{
ApplicationStage oldValue = _currentStage;
_currentStage = value;
CloseStage(oldValue);
StartStage(_currentStage);
}
}
private ApplicationStage _currentStage = ApplicationStage.BootUp;
#endregion
#region "Жизненный цикл"
private StartForm? _startFormWpf;
private ProgressForm? _progressFormWpf;
private EntityForm? _entityForm;
private Window? CurrentWpfForm { get; set; }
public delegate void ApplicationStateChangeDelegate(object sender, ApplicationStage nextStage);
private void StartStage(ApplicationStage stage)
{
switch (stage)
{
case ApplicationStage.BootUp:
throw new(
$"{CurrentStage} никогда не должен быть установлен через поле \"CurrentStage\"."
);
case ApplicationStage.InitialSettings:
_startFormWpf = ActivatorUtilities.CreateInstance<StartForm>(ServiceProvider);
_startFormWpf.WindowStartupLocation = WindowStartupLocation.CenterScreen;
_startFormWpf.Closing += Wpf_HandleFormClosing;
_startFormWpf.ApplicationStateChange += ApplicationStateChange;
_startFormWpf.Show();
CurrentWpfForm = _startFormWpf;
break;
case ApplicationStage.ImportProcess:
ProcessorExecutor executor = ActivatorUtilities.CreateInstance<ProcessorExecutor>(
ServiceProvider
);
_progressFormWpf = ActivatorUtilities.CreateInstance<ProgressForm>(ServiceProvider);
_progressFormWpf.WindowStartupLocation = WindowStartupLocation.CenterScreen;
_progressFormWpf.Closing += Wpf_HandleFormClosing;
_progressFormWpf.ApplicationStateChange += ApplicationStateChange;
_progressFormWpf.Show();
CurrentWpfForm = _progressFormWpf;
executor.Start();
break;
case ApplicationStage.Viewer:
_entityForm = ActivatorUtilities.CreateInstance<EntityForm>(ServiceProvider);
_entityForm.WindowStartupLocation = WindowStartupLocation.CenterScreen;
_entityForm.Closing += Wpf_HandleFormClosing;
_entityForm.ApplicationStateChange += ApplicationStateChange;
_entityForm.Show();
CurrentWpfForm = _entityForm;
break;
default:
throw new ArgumentOutOfRangeException();
}
}
private void CloseStage(ApplicationStage stage)
{
CurrentWpfForm = null;
switch (stage)
{
case ApplicationStage.BootUp:
// Нечего закрывать
break;
case ApplicationStage.InitialSettings:
_startFormWpf!.Hide();
_startFormWpf!.Close();
_startFormWpf = null;
break;
case ApplicationStage.ImportProcess:
_progressFormWpf!.Hide();
_progressFormWpf!.Close();
_progressFormWpf = null;
break;
case ApplicationStage.Viewer:
_entityForm!.Hide();
_entityForm!.Close();
_entityForm = null;
break;
default:
throw new ArgumentOutOfRangeException(nameof(stage));
}
}
#endregion
#region "События"
public void ApplicationStateChange(object sender, ApplicationStage nextStage)
{
if (sender?.Equals(CurrentWpfForm) ?? false)
{
CurrentStage = nextStage;
}
}
private void Wpf_HandleFormClosing(object? sender, CancelEventArgs e)
{
if (sender?.Equals(CurrentWpfForm) ?? false)
{
Shutdown();
}
else
{
e.Cancel = true;
}
}
#endregion
#region "Внедрение зависимостей"
public IServiceProvider ServiceProvider { get; }
public ApplicationSettings Settings { get; }
public ProcessorCollection Processors { get; }
public static void RegisterServices(IServiceCollection services)
{
// Контекст базы данных
services.AddEntityFrameworkNpgsql();
services.AddDbContext<Pf2eDbContext>();
//services.AddDbContextPool<Pf2eDbContext>(options =>
//{
// options.Us
//});
// Основная программа
services.AddSingleton<ImportApplication>();
// Настройки программы
services.AddSingleton<ApplicationSettings>();
// Набор процессоров
services.AddSingleton<ProcessorCollection>();
// Помощник идентификации базы данных
services.AddSingleton<DatabaseHelper>();
// Помощник создание URL-совместимых названий
services.AddTransient<SlugHelper>();
// Счётчик прогресса
services.AddSingleton<ProcessProgress>();
}
#endregion
}
internal enum ApplicationStage
{
BootUp,
InitialSettings,
ImportProcess,
Viewer,
Rules,
}

Some files were not shown because too many files have changed in this diff Show More