Compare commits

..
3 Commits
8 changed files with 345 additions and 82 deletions
+40 -3
View File
@@ -4,6 +4,7 @@ using Pf2e.Database.Model;
using Pf2e.Database.Model.Base; using Pf2e.Database.Model.Base;
using Pf2e.Database.Model.Enum; using Pf2e.Database.Model.Enum;
using Pf2e.Database.Model.Structure; using Pf2e.Database.Model.Structure;
using Action = Pf2e.Database.Model.Structure.Action;
namespace Pf2e.Common; namespace Pf2e.Common;
@@ -89,6 +90,7 @@ public class FullEntity
/// </summary> /// </summary>
private readonly ICollection<Getter> _getters = private readonly ICollection<Getter> _getters =
[ [
new Getter<Action>(EntityType.Action, context => context.Actions),
new Getter<Ancestry>( new Getter<Ancestry>(
EntityType.Ancestry, EntityType.Ancestry,
context => context.Ancestries.Include(a => a.AncestryBoosts) context => context.Ancestries.Include(a => a.AncestryBoosts)
@@ -121,15 +123,41 @@ public class FullEntity
.Include(c => c.ClassProgressions) .Include(c => c.ClassProgressions)
.Include(c => c.SubclassGroups) .Include(c => c.SubclassGroups)
), ),
new Getter<Condition>(EntityType.Condition, context => context.Conditions),
new Getter<Deity>(
EntityType.Deity,
context =>
context
.Deities.Include(d => d.DeityAttributes)
.Include(d => d.DeityDomains)
.ThenInclude(dd => dd.Domain)
.Include(d => d.DeitySpells)
.ThenInclude(ds => ds.Spell)
.Include(d => d.DeityWeapons)
.ThenInclude(dw => dw.Weapon)
.Include(d => d.CategoryEntity)
.Include(d => d.Skills)
),
new Getter<DeityCategory>(EntityType.DeityCategory, context => context.DeityCategories),
new Getter<Domain>(
EntityType.Domain,
context =>
context
.Domains.Include(d => d.DomainSpell)
.Include(d => d.AdvancedDomainSpell)
.Include(d => d.ApocryphalSpell)
.Include(d => d.AdvancedApocryphalSpell)
),
new Getter<Feat>( new Getter<Feat>(
EntityType.Feat, EntityType.Feat,
context => context =>
context.Feats.Include(f => f.Archetypes).ThenInclude(a => a.Entity).AsQueryable() context.Feats.Include(f => f.Archetypes).ThenInclude(a => a.Entity).AsQueryable()
), ),
new Getter<Spell>( new Getter<Heritage>(
EntityType.Spell, EntityType.Heritage,
context => context.Spells.Include(s => s.SpellAreas).Include(s => s.SpellDefenses) context => context.Heritages.Include(h => h.Ancestry)
), ),
new Getter<Language>(EntityType.Language, context => context.Languages),
new Getter<Ritual>( new Getter<Ritual>(
EntityType.Ritual, EntityType.Ritual,
context => context =>
@@ -139,6 +167,14 @@ public class FullEntity
.ThenInclude(c => c.Training) .ThenInclude(c => c.Training)
.AsQueryable() .AsQueryable()
), ),
new Getter<Rule>(
EntityType.Rule,
context => context.Rules.Include(r => r.Parent).Include(r => r.Children)
),
new Getter<Spell>(
EntityType.Spell,
context => context.Spells.Include(s => s.SpellAreas).Include(s => s.SpellDefenses)
),
new Getter<Subclass>( new Getter<Subclass>(
EntityType.Subclass, EntityType.Subclass,
context => context =>
@@ -147,6 +183,7 @@ public class FullEntity
.ThenInclude(t => t.Training) .ThenInclude(t => t.Training)
.Include(s => s.Entities) .Include(s => s.Entities)
), ),
new Getter<Trait>(EntityType.Trait, context => context.Traits),
]; ];
/// <summary> /// <summary>
+4 -4
View File
@@ -15,17 +15,17 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="DefaultDocumentation" Version="1.2.0" PrivateAssets="all" /> <PackageReference Include="DefaultDocumentation" Version="1.2.0" PrivateAssets="all" />
<PackageReference Include="dotenv.net" Version="3.2.1" /> <PackageReference Include="dotenv.net" Version="4.0.0" />
<PackageReference Include="EFCore.NamingConventions" Version="9.0.0" /> <PackageReference Include="EFCore.NamingConventions" Version="9.0.0" />
<PackageReference Include="linq2db" Version="5.4.1" /> <PackageReference Include="linq2db" Version="5.4.1" />
<PackageReference Include="linq2db.EntityFrameworkCore" Version="9.0.0" /> <PackageReference Include="linq2db.EntityFrameworkCore" Version="9.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.4"> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.9">
<!--<PrivateAssets>all</PrivateAssets>--> <!--<PrivateAssets>all</PrivateAssets>-->
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.4" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.9" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
<PackageReference Include="Npgsql" Version="9.0.3" /> <PackageReference Include="Npgsql" Version="9.0.4" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" /> <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
+1 -1
View File
@@ -26,7 +26,7 @@
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
<PackageReference Include="Npgsql" Version="9.0.3" /> <PackageReference Include="Npgsql" Version="9.0.4" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" /> <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
<PackageReference Include="Slugify.Core" Version="5.1.1" /> <PackageReference Include="Slugify.Core" Version="5.1.1" />
</ItemGroup> </ItemGroup>
+247
View File
@@ -0,0 +1,247 @@
using System.Collections;
using System.Reflection;
using Pf2e.Common;
using Pf2e.Database.Model;
using Pf2e.Database.Model.Base;
using Pf2e.Database.Model.Common.I18n;
using Pf2e.Database.Model.Enum;
namespace Pf2e.Web.Helpers;
/// <summary>
/// Сериализатор сущности для отображения на веб-странице.
/// </summary>
public class EntitySerializer
{
/// <summary>
/// Результат сериализации сущности.
/// </summary>
public class SerializationResult
{
public List<FieldGroup> Groups { get; set; } = [];
}
/// <summary>
/// Группа полей.
/// </summary>
public class FieldGroup
{
public string Name { get; set; } = string.Empty;
public List<FieldInfo> Fields { get; set; } = [];
}
/// <summary>
/// Информация о поле.
/// </summary>
public class FieldInfo
{
public string Name { get; set; } = string.Empty;
public string Type { get; set; } = string.Empty;
public object? Value { get; set; }
public string DisplayValue { get; set; } = string.Empty;
public bool IsImplemented { get; set; } = true;
}
/// <summary>
/// Сериализовать FullEntity.
/// </summary>
public SerializationResult Serialize(FullEntity fullEntity)
{
SerializationResult result = new();
// Группа для основных полей Entity
FieldGroup entityGroup = new() { Name = "Основные поля" };
AddFieldsToGroup(entityGroup, fullEntity.Entity);
result.Groups.Add(entityGroup);
// Группа для полей IEntity (SubEntity)
if (fullEntity.SubEntity != null)
{
FieldGroup subEntityGroup = new()
{
Name = "Поля " + fullEntity.SubEntity.GetType().Name,
};
AddFieldsToGroup(subEntityGroup, fullEntity.SubEntity);
result.Groups.Add(subEntityGroup);
}
// Группа для ссылок
FieldGroup referencesGroup = new() { Name = "Ссылки" };
AddReferencesToGroup(referencesGroup, fullEntity);
result.Groups.Add(referencesGroup);
return result;
}
/// <summary>
/// Добавить поля объекта в группу.
/// </summary>
private void AddFieldsToGroup(FieldGroup group, object obj)
{
Type type = obj.GetType();
PropertyInfo[] properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (PropertyInfo property in properties)
{
// Пропускаем навигационные свойства Entity
if (property.Name == "Entity")
continue;
FieldInfo fieldInfo = new()
{
Name = property.Name,
Type = GetTypeDisplayName(property.PropertyType),
Value = property.GetValue(obj),
};
// Обработка значения для отображения
var (displayValue, isImplemented) = FormatValue(fieldInfo.Value);
fieldInfo.DisplayValue = displayValue;
fieldInfo.IsImplemented = isImplemented;
group.Fields.Add(fieldInfo);
}
}
/// <summary>
/// Добавить ссылки в группу.
/// </summary>
private void AddReferencesToGroup(FieldGroup group, FullEntity fullEntity)
{
// Добавляем информацию о связанных сущностях
Entity entity = fullEntity.Entity;
// Traits
if (entity.Traits?.Any() == true)
{
var (displayValue, isImplemented) = FormatValue(entity.Traits);
group.Fields.Add(
new FieldInfo
{
Name = "Traits",
Type = "ICollection<Trait>",
Value = entity.Traits,
DisplayValue = displayValue,
IsImplemented = isImplemented,
}
);
}
// Keywords
if (entity.Keywords?.Any() == true)
{
var (displayValue, isImplemented) = FormatValue(entity.Keywords);
group.Fields.Add(
new FieldInfo
{
Name = "Keywords",
Type = "ICollection<Keyword>",
Value = entity.Keywords,
DisplayValue = displayValue,
IsImplemented = isImplemented,
}
);
}
// Sources
if (entity.Sources?.Any() == true)
{
var (displayValue, isImplemented) = FormatValue(entity.Sources);
group.Fields.Add(
new FieldInfo
{
Name = "Sources",
Type = "ICollection<EntitySource>",
Value = entity.Sources,
DisplayValue = displayValue,
IsImplemented = isImplemented,
}
);
}
// Externals
if (entity.Externals?.Any() == true)
{
var (displayValue, isImplemented) = FormatValue(entity.Externals);
group.Fields.Add(
new()
{
Name = "Externals",
Type = "ICollection<EntityExternal>",
Value = entity.Externals,
DisplayValue = displayValue,
IsImplemented = isImplemented,
}
);
}
}
/// <summary>
/// Получить отображаемое имя типа.
/// </summary>
private string GetTypeDisplayName(Type type)
{
if (type.IsEnum)
return "enum";
return type.Name;
}
/// <summary>
/// Форматировать значение для отображения.
/// </summary>
private (string displayValue, bool isImplemented) FormatValue(object? value)
{
if (value == null)
return ("null", true);
Type type = value.GetType();
// Обработка перечислений
if (type.IsEnum)
{
Enum enumValue = (Enum)value;
return ($"{enumValue} ({(int)(object)enumValue})", true);
}
// Обработка примитивных типов
if (type.IsPrimitive || type == typeof(string) || type == typeof(decimal))
{
return (value.ToString() ?? "null", true);
}
// Обработка IEntity
if (typeof(IEntity).IsAssignableFrom(type))
{
var entity = (IEntity)value;
var entityType = entity.Entity.Type;
var slug = entity.Entity.Slug;
var name = entity.Entity.Name[LocaleType.English];
return ($"<a href=\"/Entity/{entityType}/{slug}\">{name}</a>", true);
}
// Обработка коллекций
if (typeof(IEnumerable).IsAssignableFrom(type) && type != typeof(string))
{
var collection = (IEnumerable)value;
var items = new List<string>();
bool allImplemented = true;
foreach (var item in collection)
{
var (itemDisplay, itemImplemented) = FormatValue(item);
items.Add(itemDisplay);
if (!itemImplemented)
allImplemented = false;
}
if (items.Count == 0)
return ("(empty)", true);
return (string.Join(", ", items), allImplemented);
}
// Для остальных сложных объектов
return ($"[{type.Name}] - НЕ РЕАЛИЗОВАНО", false);
}
}
+29 -37
View File
@@ -5,41 +5,33 @@
ViewData["Title"] = "Entity"; ViewData["Title"] = "Entity";
} }
<h1>Entity</h1> <h1>Entity: @Model.FullEntity.Entity.Name</h1>
<div> @foreach (var group in Model.SerializationResult.Groups)
<h4>Class</h4> {
<hr /> <div class="card mb-4">
<dl class="row"> <div class="card-header">
<dt class="col-sm-2"> <h5>@group.Name</h5>
@Html.DisplayNameFor(model => model.Entity.Entity) </div>
</dt> <div class="card-body">
<dd class="col-sm-10"> <dl class="row">
@Html.DisplayFor(model => model.Entity.Entity.Description) @foreach (var field in group.Fields)
</dd> {
@* <dt class="col-sm-2"> *@ <dt class="col-sm-3">@field.Name</dt>
@* @Html.DisplayNameFor(model => model.Entity.Hp) *@ <dd class="col-sm-9">
@* </dt> *@ <span class="text-muted">[@field.Type]</span>
@* <dd class="col-sm-10"> *@ @if (field.IsImplemented)
@* @Html.DisplayFor(model => model.Entity.Hp) *@ {
@* </dd> *@ @Html.Raw(field.DisplayValue)
@* <dt class="col-sm-2"> *@ }
@* @Html.DisplayNameFor(model => model.Entity.Spellcasting) *@ else
@* </dt> *@ {
@* <dd class="col-sm-10"> *@ <span class="badge bg-warning text-dark">НЕ РЕАЛИЗОВАНО:</span>
@* @Html.DisplayFor(model => model.Entity.Spellcasting) *@ @Html.Raw(field.DisplayValue)
@* </dd> *@ }
@* <dt class="col-sm-2"> *@ </dd>
@* @Html.DisplayNameFor(model => model.Entity.Tradition) *@ }
@* </dt> *@ </dl>
@* <dd class="col-sm-10"> *@ </div>
@* @Html.DisplayFor(model => model.Entity.Tradition) *@ </div>
@* </dd> *@ }
@* <dt class="col-sm-2"> *@
@* @Html.DisplayNameFor(model => model.Entity.AdditionalTrainedSkills) *@
@* </dt> *@
@* <dd class="col-sm-10"> *@
@* @Html.DisplayFor(model => model.Entity.AdditionalTrainedSkills) *@
@* </dd> *@
</dl>
</div>
+21 -35
View File
@@ -5,60 +5,46 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Pf2e.Common;
using Pf2e.Database; using Pf2e.Database;
using Pf2e.Database.Model; using Pf2e.Database.Model;
using Pf2e.Database.Model.Base; using Pf2e.Database.Model.Base;
using Pf2e.Database.Model.Enum; using Pf2e.Database.Model.Enum;
using Pf2e.Database.Model.Structure; using Pf2e.Database.Model.Structure;
using Pf2e.Web.Helpers;
namespace Pf2e.Web.Pages namespace Pf2e.Web.Pages
{ {
public class EntityModel : PageModel public class EntityModel(Pf2eDbContext context) : PageModel
{ {
private readonly Pf2e.Database.Pf2eDbContext _context; private readonly Pf2eDbContext _context = context;
public EntityModel(Pf2e.Database.Pf2eDbContext context) public FullEntity FullEntity { get; set; } = null!;
public EntitySerializer.SerializationResult SerializationResult { get; set; } = null!;
public IActionResult OnGet(string type, string slug)
{ {
_context = context; // Получаем тип сущности
} EntityType entityType = EntityTypeModel.Enum<EntityType>(type);
public IEntity Entity; // Находим сущность по slug и типу
Entity? entity = _context.Entities.FirstOrDefault(e =>
e.Slug == slug && e.Type == entityType
);
public Dictionary<string, string> Fields = []; if (entity == null)
public Dictionary<string, List<Entity>> References = [];
public async Task<IActionResult> OnGetAsync(string type, string slug)
{
Entity entity =
_context
.Entities.Where(e =>
e.Slug == slug && e.Type == EntityTypeModel.Enum<EntityType>(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");
/*
if (id == null)
{ {
return NotFound(); return NotFound();
} }
var class = await _context.Classes.FirstOrDefaultAsync(m => m.Id == id); // Создаём FullEntity
FullEntity = new(_context, entity);
if (class is not null) // Сериализуем для отображения
{ EntitySerializer serializer = new();
Class = class; SerializationResult = serializer.Serialize(FullEntity);
return Page(); return Page();
}
*/
return NotFound();
} }
} }
} }
+1 -1
View File
@@ -45,7 +45,7 @@
@Html.DisplayFor(modelItem => item.Id) @Html.DisplayFor(modelItem => item.Id)
</td> </td>
<td> <td>
<a asp-page="./Details" asp-route-id="@item.Id"> <a asp-page="./Entity" asp-route-type="@item.Type" asp-route-slug="@item.Slug">
@Html.DisplayFor(modelItem => item.Name[LocaleType.English]) @Html.DisplayFor(modelItem => item.Name[LocaleType.English])
</a> </a>
</td> </td>
+2 -1
View File
@@ -14,10 +14,11 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="9.0.0" /> <PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="9.0.0" />
<PackageReference Include="Npgsql" Version="9.0.3" /> <PackageReference Include="Npgsql" Version="9.0.4" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" /> <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Common\Pf2e.Common.csproj" />
<ProjectReference Include="..\Database\Pf2e.Database.csproj" /> <ProjectReference Include="..\Database\Pf2e.Database.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>