Оно собралось!

This commit is contained in:
2025-05-13 23:12:24 +04:00
parent 83ee914991
commit 1b76893b32
45 changed files with 5032 additions and 4763 deletions
+3 -1
View File
@@ -95,6 +95,8 @@ internal class ProcessorCollection
/// </summary>
public void SortItems()
{
Items.ForEach(item => item.Priority = -1);
Dictionary<string, int> priorities = [];
while (priorities.Count < Items.Count)
@@ -115,7 +117,7 @@ internal class ProcessorCollection
continue;
}
item.Priority = Math.Min(
item.Priority = Math.Max(
dependencyPriorities.Any() ? dependencyPriorities.Max() + 1 : 1,
(int)item.Group * 100
);
+104
View File
@@ -0,0 +1,104 @@
namespace Pf2eDataScrapper.Forms;
partial class ProgressForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
ProcessorsProgressBar = new ProgressBar();
ProcessorsLabel = new Label();
LocalLabel = new Label();
LocalProgressBar = new ProgressBar();
MessageListView = new ListView();
SuspendLayout();
//
// ProcessorsProgressBar
//
ProcessorsProgressBar.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right;
ProcessorsProgressBar.Location = new Point(12, 27);
ProcessorsProgressBar.Name = "ProcessorsProgressBar";
ProcessorsProgressBar.Size = new Size(439, 23);
ProcessorsProgressBar.TabIndex = 0;
//
// ProcessorsLabel
//
ProcessorsLabel.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right;
ProcessorsLabel.Location = new Point(12, 9);
ProcessorsLabel.Name = "ProcessorsLabel";
ProcessorsLabel.Size = new Size(439, 15);
ProcessorsLabel.TabIndex = 1;
ProcessorsLabel.Text = "ProcessorsLabel";
//
// LocalLabel
//
LocalLabel.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right;
LocalLabel.Location = new Point(12, 59);
LocalLabel.Name = "LocalLabel";
LocalLabel.Size = new Size(439, 15);
LocalLabel.TabIndex = 2;
LocalLabel.Text = "LocalLabel";
//
// LocalProgressBar
//
LocalProgressBar.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right;
LocalProgressBar.Location = new Point(12, 77);
LocalProgressBar.Name = "LocalProgressBar";
LocalProgressBar.Size = new Size(439, 23);
LocalProgressBar.TabIndex = 3;
//
// MessageListView
//
MessageListView.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
MessageListView.Location = new Point(12, 114);
MessageListView.Name = "MessageListView";
MessageListView.Size = new Size(439, 316);
MessageListView.TabIndex = 4;
MessageListView.UseCompatibleStateImageBehavior = false;
MessageListView.View = View.List;
//
// ProgressForm
//
AutoScaleDimensions = new SizeF(7F, 15F);
AutoScaleMode = AutoScaleMode.Font;
ClientSize = new Size(463, 442);
Controls.Add(MessageListView);
Controls.Add(LocalProgressBar);
Controls.Add(LocalLabel);
Controls.Add(ProcessorsLabel);
Controls.Add(ProcessorsProgressBar);
Name = "ProgressForm";
Text = "ProgressForm";
ResumeLayout(false);
}
#endregion
private ProgressBar ProcessorsProgressBar;
private Label ProcessorsLabel;
private Label LocalLabel;
private ProgressBar LocalProgressBar;
private ListView MessageListView;
}
+50
View File
@@ -0,0 +1,50 @@
using Pf2eDataScrapper.Processors;
namespace Pf2eDataScrapper.Forms;
internal partial class ProgressForm : Form
{
public ProcessProgress Progress { get; }
public ProgressForm(ProcessProgress progress)
{
Progress = progress;
InitializeComponent();
Progress.ProgressUpdate += Progress_ProgressUpdate;
Progress.ProgressMessage += Progress_ProgressMessage;
}
private void Progress_ProgressUpdate(ProcessProgress sender)
{
if (InvokeRequired)
{
Invoke(Progress_ProgressUpdate, sender);
return;
}
ProcessorsLabel.Text = sender.Processors.ToString();
ProcessorsProgressBar.Maximum = sender.Processors.Count;
ProcessorsProgressBar.Value = sender.Processors.Current;
LocalLabel.Text = sender.Local.ToString();
LocalProgressBar.Maximum = sender.Local.Count;
LocalProgressBar.Value = sender.Local.Current;
Application.DoEvents();
}
private void Progress_ProgressMessage(ProcessProgress sender, string message)
{
if (InvokeRequired)
{
Invoke(Progress_ProgressMessage, sender, message);
return;
}
MessageListView.Items.Add(message);
Application.DoEvents();
}
}
+120
View File
@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>
+2
View File
@@ -102,10 +102,12 @@ partial class StartForm
ProcessorsListView.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
ProcessorsListView.CheckBoxes = true;
ProcessorsListView.Location = new Point(6, 48);
ProcessorsListView.MultiSelect = false;
ProcessorsListView.Name = "ProcessorsListView";
ProcessorsListView.Size = new Size(606, 548);
ProcessorsListView.TabIndex = 2;
ProcessorsListView.UseCompatibleStateImageBehavior = false;
ProcessorsListView.View = View.SmallIcon;
//
// SelectAllButton
//
+10 -6
View File
@@ -1,19 +1,18 @@
using System.ComponentModel;
using System.Reflection;
using Newtonsoft.Json.Linq;
using Pf2eDataScrapper.App;
namespace Pf2eDataScrapper.Forms;
internal partial class StartForm : Form
{
private readonly ProcessorCollection _processorCollection;
private ProcessorCollection ProcessorCollection { get; }
public StartForm(ProcessorCollection processorCollection)
{
InitializeComponent();
ProcessorCollection = processorCollection;
_processorCollection = processorCollection;
InitializeComponent();
}
private void StartForm_Load(object sender, EventArgs e)
@@ -25,7 +24,7 @@ internal partial class StartForm : Form
{
Dictionary<ProcessorGroupEnum, List<ProcessorCollectionItem>> items = [];
foreach (ProcessorCollectionItem item in _processorCollection.AllItems)
foreach (ProcessorCollectionItem item in ProcessorCollection.AllItems)
{
items.TryAdd(item.Group, []);
items[item.Group].Add(item);
@@ -40,7 +39,12 @@ internal partial class StartForm : Form
continue;
}
string name = Enum.GetDescription(group);
string name =
group
.GetType()
.GetField(group.ToString())
?.GetCustomAttribute<DescriptionAttribute>()
?.Description ?? group.ToString();
ListViewGroup listViewGroup = new(name);
ProcessorsListView.Groups.Add(listViewGroup);
+108 -20
View File
@@ -2,10 +2,6 @@
using Pf2eDataScrapper.App;
using Pf2eDataScrapper.Forms;
using Pf2eDataScrapper.Helpers;
using Pf2eDataScrapper.Importers;
using Pf2eDataScrapper.Importers.Implementations.Aonprd;
using Pf2eDataScrapper.Importers.Implementations.FoundryEn;
using Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry;
using Pf2eDataScrapper.Processors;
using Pf2eModel;
using Slugify;
@@ -24,14 +20,27 @@ internal sealed class ImportApplication : ApplicationContext
Settings = settings;
Processors = processors;
CreateStartForm();
CurrentStage = ApplicationStage.InitialSettings;
}
#region "Настройки"
#region "Поля"
public IServiceProvider ServiceProvider { get; }
public ApplicationSettings Settings { get; }
public ProcessorCollection Processors { get; }
/// <summary>
/// Текущий этап выполнения программы.
/// </summary>
public ApplicationStage CurrentStage
{
get => _currentStage;
set
{
ApplicationStage oldValue = _currentStage;
_currentStage = value;
CloseStage(oldValue);
StartStage(_currentStage);
}
}
private ApplicationStage _currentStage = ApplicationStage.BootUp;
#endregion
@@ -39,33 +48,104 @@ internal sealed class ImportApplication : ApplicationContext
private StartForm? _startForm;
private ProgressForm? _progressForm;
#endregion
#region "Жизненный цикл"
private void CreateStartForm()
private void StartStage(ApplicationStage stage)
{
_startForm = ActivatorUtilities.CreateInstance<StartForm>(ServiceProvider);
_startForm.StartPosition = FormStartPosition.CenterParent;
_startForm.StartButtonPressed += StartProcess;
_startForm.Show();
switch (stage)
{
case ApplicationStage.BootUp:
throw new(
$"{CurrentStage} никогда не должен быть установлен через поле \"CurrentStage\"."
);
case ApplicationStage.InitialSettings:
_startForm = ActivatorUtilities.CreateInstance<StartForm>(ServiceProvider);
_startForm.StartPosition = FormStartPosition.CenterParent;
_startForm.StartButtonPressed += () =>
{
CurrentStage = ApplicationStage.ProcessorsWorking;
};
_startForm.FormClosing += HandleFormClosing;
_startForm.Show();
break;
case ApplicationStage.ProcessorsWorking:
ProcessorExecutor executor = ActivatorUtilities.CreateInstance<ProcessorExecutor>(
ServiceProvider
);
_progressForm = ActivatorUtilities.CreateInstance<ProgressForm>(ServiceProvider);
_progressForm.StartPosition = FormStartPosition.CenterParent;
_progressForm.FormClosing += HandleFormClosing;
_progressForm.Show();
executor.Start();
break;
case ApplicationStage.Viewer:
break;
default:
throw new ArgumentOutOfRangeException();
}
Application.DoEvents();
}
private void StartProcess()
private void CloseStage(ApplicationStage stage)
{
_startForm!.Close();
switch (stage)
{
case ApplicationStage.BootUp:
// Нечего закрывать
break;
ProcessorExecutor executor = ActivatorUtilities.CreateInstance<ProcessorExecutor>(
ServiceProvider
);
case ApplicationStage.InitialSettings:
_startForm!.Hide();
_startForm!.Close();
_startForm = null;
break;
executor.Start();
case ApplicationStage.ProcessorsWorking:
break;
case ApplicationStage.Viewer:
break;
default:
throw new ArgumentOutOfRangeException();
}
Application.DoEvents();
}
private void HandleFormClosing(object? sender, FormClosingEventArgs e)
{
switch (sender)
{
case StartForm:
e.Cancel = CurrentStage != ApplicationStage.InitialSettings;
if (!e.Cancel)
{
ExitThread();
}
return;
}
}
#endregion
#region "Внедрение зависимостей"
public IServiceProvider ServiceProvider { get; }
public ApplicationSettings Settings { get; }
public ProcessorCollection Processors { get; }
public static void RegisterServices(IServiceCollection services)
{
// Контекст базы данных
@@ -92,3 +172,11 @@ internal sealed class ImportApplication : ApplicationContext
#endregion
}
internal enum ApplicationStage
{
BootUp,
InitialSettings,
ProcessorsWorking,
Viewer,
}
@@ -17,16 +17,6 @@ internal abstract class AbstractImporter
/// </summary>
public abstract string Slug { get; }
/// <summary>
/// Репозиторий для данных, загруженных из необходимых источников.
/// </summary>
public abstract AbstractImporterRepository Repository { get; }
/// <summary>
/// Компендиум данных импортёра.
/// </summary>
public abstract AbstractCompendium Compendium { get; }
/// <summary>
/// Путь к директории с данными для импортёра.
/// </summary>
@@ -8,8 +8,7 @@ namespace Pf2eDataScrapper.Importers.Implementations.Aonprd;
/// <summary>
/// Класс обработки данных с сайта Архивов Нефеса.
/// </summary>
internal class AonprdImporter(AonprdRepository repository, AonprdCompendium compendium)
: AbstractImporter
internal class AonprdImporter : AbstractImporter
{
/// <summary>
/// Название внешней ссылки.
@@ -22,12 +21,6 @@ internal class AonprdImporter(AonprdRepository repository, AonprdCompendium comp
/// <inheritdoc/>
public override string Slug => "aonprd";
/// <inheritdoc/>
public override AonprdRepository Repository { get; } = repository;
/// <inheritdoc/>
public override AonprdCompendium Compendium { get; } = compendium;
#region "Внедрение зависимостей"
public static void RegisterServices(IServiceCollection services)
@@ -53,11 +53,7 @@ internal partial class ClassProcessor(
foreach (AttributeValues attribute in attributes)
{
ClassKeyAbility classKeyAbility = new ClassKeyAbility()
{
Class = Object,
Value = attribute,
};
ClassKeyAbility classKeyAbility = new() { Class = Object, Value = attribute };
Context.ClassKeyAbilities.Add(classKeyAbility);
Object.ClassKeyAbilities.Add(classKeyAbility);
@@ -1,6 +1,5 @@
using Microsoft.Extensions.DependencyInjection;
using Pf2eDataScrapper.Importers.Implementations.FoundryEn.Compendium;
using Pf2eDataScrapper.Importers.Implementations.FoundryEn.DataBuilder;
using Pf2eDataScrapper.Importers.Repositories;
namespace Pf2eDataScrapper.Importers.Implementations.FoundryEn;
@@ -8,8 +7,7 @@ namespace Pf2eDataScrapper.Importers.Implementations.FoundryEn;
/// <summary>
/// Импортёр для модуля Pf2e FoundryVTT.
/// </summary>
internal class FoundryEnImporter(FoundryEnRepository repository, FoundryEnCompendium compendium)
: AbstractImporter
internal class FoundryEnImporter : AbstractImporter
{
/// <summary>
/// Название внешней ссылки.
@@ -26,13 +24,6 @@ internal class FoundryEnImporter(FoundryEnRepository repository, FoundryEnCompen
/// </summary>
public override string Slug => "foundry-en";
public override FoundryEnRepository Repository { get; } = repository;
/// <summary>
/// Загрузчик данных компендиума.
/// </summary>
public override FoundryEnCompendium Compendium { get; } = compendium;
#region "Внедрение зависимостей"
public static void RegisterServices(IServiceCollection services)
@@ -1,11 +1,10 @@
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.ComponentModel;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json.Linq;
using Pf2eDataScrapper.App;
using Pf2eDataScrapper.Importers;
using Pf2eDataScrapper.Importers.Implementations.Aonprd;
using Pf2eDataScrapper.Importers.Implementations.Aonprd.Compendium;
using Pf2eDataScrapper.Importers.Implementations.FoundryEn;
namespace Pf2eDataScrapper.Processors;
@@ -26,28 +25,46 @@ internal sealed class ProcessorExecutor(
Progress.Processors.Reset(Collection.Items.Count);
foreach (ProcessorCollectionItem item in Collection.Items)
{
Progress.Processors.Start(item.Description);
Process(item);
Progress.Processors.End();
}
Progress.Processors.Complete();
StartItem();
}
private void Process(ProcessorCollectionItem item)
private int _currentIndex = 0;
private void StartItem()
{
Thread thread = new(() =>
ProcessorCollectionItem item = Collection.Items[_currentIndex];
Progress.Processors.Start(item.Description);
BackgroundWorker worker = new();
worker.DoWork += (_, _) =>
{
foreach (IProcessor processor in IterateProcessors(item))
{
processor.Process();
processor.Save();
}
});
};
worker.RunWorkerCompleted += Continue;
worker.RunWorkerAsync();
}
private void Continue(object? sender, RunWorkerCompletedEventArgs e)
{
Progress.Processors.End();
_currentIndex++;
if (_currentIndex < Collection.Items.Count)
{
StartItem();
}
else
{
Progress.Processors.Complete();
}
}
private IEnumerable<IProcessor> IterateProcessors(ProcessorCollectionItem item)
@@ -72,19 +89,19 @@ internal sealed class ProcessorExecutor(
{
ProcessorGroupEnum.AonprdPreparation
or ProcessorGroupEnum.AonprdEntities
or ProcessorGroupEnum.AonprdSubclasses => typeof(AonprdImporter),
or ProcessorGroupEnum.AonprdSubclasses => typeof(AonprdCompendium),
ProcessorGroupEnum.FoundryEnPreparation or ProcessorGroupEnum.FoundryEnEntities =>
typeof(FoundryEnImporter),
typeof(FoundryEnCompendium),
_ => throw new($"Не могу выбрать импортёр для группы {item.Group}"),
};
AbstractImporter importer = (AbstractImporter)
AonprdCompendium compendium = (AonprdCompendium)
ActivatorUtilities.CreateInstance(ServiceProvider, importerType);
Progress.Local.Reset(importer.Compendium.Count(item.InnerType!));
Progress.Local.Reset(compendium.Count(item.InnerType!));
foreach (
(AbstractCompendiumItem compendiumItem, JObject json) in importer.Compendium.Iterate(
(AbstractCompendiumItem compendiumItem, JObject json) in compendium.Iterate(
item.InnerType!
)
)
+7 -3
View File
@@ -17,16 +17,20 @@ internal static class Program
ApplicationConfiguration.Initialize();
Application.SetHighDpiMode(HighDpiMode.SystemAware);
/*
#pragma warning disable WFO5001
Application.SetColorMode(SystemColorMode.System);
#pragma warning restore WFO5001
*/
IServiceCollection services = RegisterServices();
IServiceProvider serviceProvider = services.BuildServiceProvider();
Application.Run(ActivatorUtilities.CreateInstance<ImportApplication>(serviceProvider));
ImportApplication app = ActivatorUtilities.CreateInstance<ImportApplication>(
serviceProvider
);
Application.Run(app);
}
private static IServiceCollection RegisterServices()