Попытки подружить источники...

This commit is contained in:
2025-06-04 16:50:51 +04:00
parent ea1dcb1595
commit 2ab05ee592
3 changed files with 110 additions and 42 deletions
@@ -36,9 +36,16 @@ internal abstract class AbstractEntityProcessor(
AonprdBuilderGlobals globals AonprdBuilderGlobals globals
) : AbstractInputProcessor(input) ) : AbstractInputProcessor(input)
{ {
/// <inheritdoc cref="Pf2eDbContext"/>
public Pf2eDbContext Context { get; } = new(); public Pf2eDbContext Context { get; } = new();
/// <inheritdoc cref="StringHelper"/>
public StringHelper StringHelper { get; } = stringHelper; public StringHelper StringHelper { get; } = stringHelper;
/// <inheritdoc cref="DatabaseHelper"/>
public DatabaseHelper Helper { get; } = helper; public DatabaseHelper Helper { get; } = helper;
/// <inheritdoc cref="AonprdBuilderGlobals"/>
public AonprdBuilderGlobals Globals { get; } = globals; public AonprdBuilderGlobals Globals { get; } = globals;
/// <summary> /// <summary>
@@ -86,7 +93,7 @@ internal abstract class AbstractEntityProcessor(
{ {
if (Globals.MergeSources.ContainsKey(Input.GetRequired<string>("id"))) if (Globals.MergeSources.ContainsKey(Input.GetRequired<string>("id")))
{ {
throw new SkipItemException(Input.GetRequired<string>("name"), "помечено на слияние."); throw new SkipItemException(GetEntityName(), "помечено на слияние.");
} }
} }
@@ -145,6 +152,12 @@ internal abstract class AbstractEntityProcessor(
#region "Генерация сущности" #region "Генерация сущности"
/// <summary>
/// Получает имя сущности из входных данных.
/// </summary>
/// <returns>Имя сущности.</returns>
protected virtual string GetEntityName() => Input.GetRequired<string>("name");
/// <summary> /// <summary>
/// Генерация основных полей корневой сущности. /// Генерация основных полей корневой сущности.
/// </summary> /// </summary>
@@ -156,7 +169,7 @@ internal abstract class AbstractEntityProcessor(
} }
Entity.Type = Type; Entity.Type = Type;
Entity.Name[LocaleType.English] = Input.GetRequired<string>("name"); Entity.Name[LocaleType.English] = GetEntityName();
Entity.Img = ""; Entity.Img = "";
Entity.Description = GetDescription(); Entity.Description = GetDescription();
Entity.Rarity = Input.EnumOptional("rarity", EntityRarity.Common); Entity.Rarity = Input.EnumOptional("rarity", EntityRarity.Common);
@@ -1,9 +1,7 @@
using Newtonsoft.Json.Linq; using Pf2eDataScrapper.Helpers;
using Pf2eDataScrapper.Helpers;
using Pf2eDataScrapper.Importers.Implementations.FoundryEn.Helpers; using Pf2eDataScrapper.Importers.Implementations.FoundryEn.Helpers;
using Pf2eDataScrapper.Processors; using Pf2eDataScrapper.Processors;
using Pf2eDataScrapper.Processors.Attributes; using Pf2eDataScrapper.Processors.Attributes;
using Pf2eDataScrapper.Processors.Collection;
using Pf2eModel; using Pf2eModel;
using Pf2eModel.Model.Enum; using Pf2eModel.Model.Enum;
@@ -17,7 +15,7 @@ namespace Pf2eDataScrapper.Importers.Implementations.FoundryEn.Processors.Entiti
Group = ProcessorGroupEnum.FoundryEnEntities, Group = ProcessorGroupEnum.FoundryEnEntities,
Active = false Active = false
)] )]
[ProcessorDependencies(["foundry-en:compendium"])] [ProcessorDependencies(["foundry-en:compendium", "foundry-en:external"])]
internal class ActionProcessor( internal class ActionProcessor(
DatabaseHelper databaseHelper, DatabaseHelper databaseHelper,
Pf2eDbContext context, Pf2eDbContext context,
@@ -1,105 +1,162 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Windows.Documents;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using Pf2eDataScrapper.Helpers; using Pf2eDataScrapper.Helpers;
using Pf2eDataScrapper.Importers.Implementations.FoundryEn.Helpers; using Pf2eDataScrapper.Importers.Implementations.FoundryEn.Helpers;
using Pf2eDataScrapper.Processors; using Pf2eDataScrapper.Processors;
using Pf2eDataScrapper.Processors.Attributes;
using Pf2eDataScrapper.Processors.Definitions; using Pf2eDataScrapper.Processors.Definitions;
using Pf2eModel; using Pf2eModel;
using Pf2eModel.Model; using Pf2eModel.Model;
using Pf2eModel.Model.Base; using Pf2eModel.Model.Base;
using Pf2eModel.Model.Base.Source;
using Pf2eModel.Model.Base.Targets; using Pf2eModel.Model.Base.Targets;
using Pf2eModel.Model.Common.I18n; using Pf2eModel.Model.Common.I18n;
using Pf2eModel.Model.Enum; using Pf2eModel.Model.Enum;
using Pf2eModel.Model.Structure;
namespace Pf2eDataScrapper.Importers.Implementations.FoundryEn.Processors; namespace Pf2eDataScrapper.Importers.Implementations.FoundryEn.Processors;
/// <summary>
/// Абстрактный класс-обработчик сущностей, полученных из молуля Foundry pf2e.
/// Класс предназначен для обработки данных с помощью различных обработчиков,
/// для которых указан атрибут <see cref="ProcessorAttribute"/>.
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <param name="databaseHelper">[DI] <inheritdoc cref="DatabaseHelper"/></param>
/// <param name="context">[DI] <inheritdoc cref="Pf2eDbContext"/></param>
/// <param name="enGlobals">[DI] <inheritdoc cref="FoundryEnGlobals"/></param>
/// <param name="input">Объект обработки вводимого JSON.</param>
internal abstract class FoundryEnAbstractProcessor<TEntity>( internal abstract class FoundryEnAbstractProcessor<TEntity>(
DatabaseHelper databaseHelper, DatabaseHelper databaseHelper,
Pf2eDbContext context, Pf2eDbContext context,
FoundryEnGlobals enGlobals, FoundryEnGlobals enGlobals,
ProcessorInput input ProcessorInput input
) : AbstractInputProcessor(input) ) : AbstractInputProcessor(input)
where TEntity : class, IEntity? where TEntity : class, IEntity
{ {
/// <inheritdoc cref="DatabaseHelper"/>
protected DatabaseHelper Helper { get; } = databaseHelper; protected DatabaseHelper Helper { get; } = databaseHelper;
/// <inheritdoc cref="Pf2eDbContext"/>
public Pf2eDbContext Context { get; } = context; public Pf2eDbContext Context { get; } = context;
/// <inheritdoc cref="FoundryEnGlobals"/>
public FoundryEnGlobals EnGlobals { get; } = enGlobals; public FoundryEnGlobals EnGlobals { get; } = enGlobals;
/// <summary>
/// Тип сущности.
/// </summary>
public abstract EntityType Type { get; } public abstract EntityType Type { get; }
/// <summary>
/// Представление сущности, которое будет использоваться для обработки данных.
/// </summary>
public EntityView View { get; private set; } = null!; public EntityView View { get; private set; } = null!;
/// <summary>
/// Основная сущность, которая будет использоваться для обработки данных.
/// </summary>
public Entity Entity => View.Entity; public Entity Entity => View.Entity;
/// <summary>
/// Подчинённая сущность, которая будет использоваться для обработки данных.
/// </summary>
public TEntity Object => public TEntity Object =>
View.SubEntity as TEntity View.SubEntity as TEntity
?? throw new InvalidOperationException( ?? throw new InvalidOperationException(
$"Сущность {View.SubEntity} некорректного типа, ожидался тип {typeof(TEntity)}." $"Сущность {View.SubEntity} некорректного типа, ожидался тип {typeof(TEntity)}."
); );
public virtual void ResolveEntity() /// <summary>
/// Разрешает сущность, основываясь на входных данных.
/// </summary>
/// <returns>Идентификатор сущности.</returns>
public virtual int ResolveEntity()
{ {
string name = Input.GetRequired<string>("name"); string name = GetEntityName();
try try
{ {
int id = Context List<Entity> entities = Context
.Entities.Include(e => e.Name) .Entities.AsNoTracking()
.Include(e => e.Traits)
.ThenInclude(t => t.Entity)
.Where(e => Pf2eDbContext.LocaleSearch(e.Name, name) && e.Type == Type) .Where(e => Pf2eDbContext.LocaleSearch(e.Name, name) && e.Type == Type)
.Select(e => e.Id) .ToList();
.FirstOrDefault();
View = new EntityView(Context, Type, id); switch (entities.Count)
{
case 0:
throw new("сущность не найдена.");
case 1:
return entities[0].Id;
}
List<string> inputTraits = Input
.GetRequired<JArray>("system.traits.value")
.Select(token => token.ToString())
.Order()
.ToList();
foreach (Entity entity in entities)
{
List<string> entityTraits = entity
.Traits.Select(t => t.Entity.Name[LocaleType.English])
.Order()
.ToList();
if (entityTraits.SequenceEqual(inputTraits))
{
return entity.Id;
}
}
} }
catch (Exception ex) catch (Exception ex)
{ {
throw new SkipItemException(name, ex.Message); throw new SkipItemException(name, ex.Message);
} }
throw new SkipItemException(
name,
$"Несколько сущностей \"{name}\" найдено, но ни одна не совпадает с входными данными по признакам."
);
} }
/// <inheritdoc/>
public override void Process() public override void Process()
{ {
ResolveEntity(); View = new(Context, Type, ResolveEntity());
AddExternal();
}
/// <inheritdoc/>
public override void Save()
{
Context.SaveChanges();
}
/// <summary>
/// Получает имя сущности из входных данных.
/// </summary>
/// <returns>Имя сущности.</returns>
protected virtual string GetEntityName() => Input.GetRequired<string>("name");
/// <summary>
/// Генерация внешних ссылок сущности.
/// </summary>
protected virtual void AddExternal()
{
// External // External
EntityExternal external = new() EntityExternal external = new()
{ {
Entity = Entity, EntityId = Entity.Id,
Target = EnGlobals.FoundryTarget, TargetId = EnGlobals.FoundryTarget.Id,
ExternalId = Input.GetOptional<string>("_id"), ExternalId = Input.GetOptional<string>("_id"),
Suffix = Input.GetRequired<string>("_id"), Suffix = Input.GetRequired<string>("_id"),
Md5 = Input.Md5(), Md5 = Input.Md5(),
}; };
Context.EntityExternals.Add(external); Context.EntityExternals.Add(external);
} }
public override void Save()
{
Context.SaveChanges();
}
protected Source GetSource(string name)
{
Source? source = Context.Sources.FirstOrDefault(s => s.Name == name);
if (source != null)
{
return source;
}
source = new() { Name = name };
Context.Sources.Add(source);
return source;
}
} }