Глобальный рефакторинг пространств имён
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user