diff --git a/DataImporterTool/DataImporterTool.csproj b/DataImporterTool/DataImporterTool.csproj
new file mode 100644
index 0000000..8e6af23
--- /dev/null
+++ b/DataImporterTool/DataImporterTool.csproj
@@ -0,0 +1,23 @@
+
+
+
+ WinExe
+ net5.0-windows
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/DataImporterTool/Dialogs/DialogService.cs b/DataImporterTool/Dialogs/DialogService.cs
new file mode 100644
index 0000000..0ba0796
--- /dev/null
+++ b/DataImporterTool/Dialogs/DialogService.cs
@@ -0,0 +1,27 @@
+using System.Windows.Forms;
+
+#nullable enable
+namespace DataImporterTool.Dialogs
+{
+ public class DialogService : IDialogService
+ {
+ public string? ChooseFolder()
+ {
+ using (var fbd = new FolderBrowserDialog())
+ {
+ var result = fbd.ShowDialog();
+ if (result == DialogResult.OK && !string.IsNullOrWhiteSpace(fbd.SelectedPath))
+ {
+ return fbd.SelectedPath;
+ }
+ }
+
+ return null;
+ }
+
+ public void ShowWarning(string warningMessage)
+ {
+ MessageBox.Show(warningMessage, "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ }
+ }
+}
\ No newline at end of file
diff --git a/DataImporterTool/Dialogs/IDialogService.cs b/DataImporterTool/Dialogs/IDialogService.cs
new file mode 100644
index 0000000..ba209ed
--- /dev/null
+++ b/DataImporterTool/Dialogs/IDialogService.cs
@@ -0,0 +1,10 @@
+#nullable enable
+namespace DataImporterTool.Dialogs
+{
+ public interface IDialogService
+ {
+ string? ChooseFolder();
+
+ void ShowWarning(string warningMessage);
+ }
+}
\ No newline at end of file
diff --git a/DataImporterTool/IMainFormView.cs b/DataImporterTool/IMainFormView.cs
new file mode 100644
index 0000000..f2307e1
--- /dev/null
+++ b/DataImporterTool/IMainFormView.cs
@@ -0,0 +1,33 @@
+using System;
+
+namespace DataImporterTool
+{
+ public interface IMainFormView
+ {
+ string MongoDbConnectionString { get; set; }
+
+ string JsonFolderPath { get; set; }
+
+ string StatusInformation { get; set; }
+
+ void ShowJsonFiles(string[] allJsonFiles);
+
+ void ShowJsonAccountFiles(string[] accountJsonFiles);
+
+ void ShowJsonTankFiles(string[] tankJsonFiles);
+
+ void ClearLog();
+
+ void AppendLogRow(string logInfo);
+
+ void SetProcessPercentage(int percentage);
+
+ event Action OpenJsonFolderClick;
+
+ event Action AddFilesAsAccountsInfoList;
+
+ event Action AddFilesAsTanksInfoList;
+
+ event Action StartJsonConvert;
+ }
+}
\ No newline at end of file
diff --git a/DataImporterTool/Importer/ImportProgress.cs b/DataImporterTool/Importer/ImportProgress.cs
new file mode 100644
index 0000000..b18a2d7
--- /dev/null
+++ b/DataImporterTool/Importer/ImportProgress.cs
@@ -0,0 +1,23 @@
+using System;
+
+namespace DataImporterTool.Importer
+{
+ public class ImportProgress
+ {
+ public int FilesConverted { get; set; }
+
+ public int Progress { get; set; }
+
+ public string AccountFileConverted { get; set; }
+
+ public string TankFileConverted { get; set; }
+
+ public string AccountName { get; set; }
+
+ public DateTime LastBattle { get; set; }
+
+ public long BattlesCount { get; set; }
+
+ public int TanksCount { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/DataImporterTool/Importer/JsonFilesImporter.cs b/DataImporterTool/Importer/JsonFilesImporter.cs
new file mode 100644
index 0000000..5c4dd44
--- /dev/null
+++ b/DataImporterTool/Importer/JsonFilesImporter.cs
@@ -0,0 +1,175 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using AutoMapper;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using Newtonsoft.Json;
+using WotBlitzStatisticsPro.Common;
+using WotBlitzStatisticsPro.Common.Model;
+using WotBlitzStatisticsPro.DataAccess;
+using WotBlitzStatisticsPro.DataAccess.Model.Accounts;
+using WotBlitzStatisticsPro.Logic;
+using WotBlitzStatisticsPro.Logic.AccountInformationPipeline;
+using WotBlitzStatisticsPro.Logic.AccountInformationPipeline.OperationContext;
+using WotBlitzStatisticsPro.Logic.AccountInformationPipeline.Operations;
+using WotBlitzStatisticsPro.Logic.Pipeline;
+using WotBlitzStatisticsPro.WgApiClient;
+using WotBlitzStatisticsPro.WgApiClient.Model;
+
+namespace DataImporterTool.Importer
+{
+ public class JsonFilesImporter
+ {
+ private const RealmType Realm = RealmType.Eu;
+
+ private IOperationFactory _operationFactory = null;
+ private IMapper _mapper = null;
+
+ public async Task Import(
+ string mongoConnectionString,
+ string folderPath,
+ string[] accountsFiles,
+ string[] tankFiles,
+ IProgress progress)
+ {
+ if (accountsFiles == null || tankFiles == null || accountsFiles.Length == 0 || accountsFiles.Length != tankFiles.Length)
+ {
+ return;
+ }
+
+ SetupOperations(mongoConnectionString);
+
+ for (int i = 0; i < accountsFiles.Length; i++)
+ {
+ var saved = await GatherAndSaveAccountInformation(
+ Path.Combine(folderPath, accountsFiles[i]),
+ Path.Combine(folderPath, tankFiles[i]));
+
+ var p = new ImportProgress
+ {
+ Progress = 100 * (i+1) / accountsFiles.Length,
+ TankFileConverted = tankFiles[i],
+ AccountFileConverted = accountsFiles[i],
+ AccountName = saved.Nickname,
+ FilesConverted = i+1,
+ LastBattle = saved.LastBattleTime,
+ BattlesCount = saved.Battles,
+ TanksCount = saved.Tanks?.Count ?? 0
+ };
+ progress.Report(p);
+ }
+ }
+
+ private async Task GatherAndSaveAccountInformation(string accountFileName, string tanksFileName)
+ {
+ var contextData = new AccountInformationPipelineContextData();
+
+ await SetAccountInfo(accountFileName, contextData);
+ await SetTanksInfo(tanksFileName, contextData);
+
+ var context = new OperationContext(new AccountRequest(contextData.AccountInfo?.AccountId ?? -1, Realm, RequestLanguage.En));
+ context.AddOrReplace(contextData);
+ var pipeline = new Pipeline(_operationFactory);
+
+ pipeline
+ .AddOperation()
+ .AddOperation()
+ .AddOperation()
+ ;
+
+ var firstOperation = pipeline.Build();
+ if (firstOperation != null)
+ {
+ await firstOperation
+ .Invoke(context, null)
+ .ConfigureAwait(false);
+ }
+ return contextData?.Response ?? new AccountInfoResponse();
+ }
+
+ private void SetupOperations(
+ string mongoConnectionString)
+ {
+ string databaseName = "WotBlitzStatisticsPro";
+
+ var settings = new MongoSettings {ConnectionString = mongoConnectionString, DatabaseName = databaseName};
+
+ var serviceProvider = new ServiceCollection()
+ .AddAutoMapper(typeof(WotBlitzStatisticsLogicInstaller))
+ .AddLogging(configure => configure.AddConsole())
+ .AddSingleton(settings)
+ .AddTransient()
+ .AddTransient()
+ .AddTransient()
+ .AddTransient()
+ .AddTransient()
+ .AddTransient(sp =>
+ new ServiceProviderOperationFactory(sp))
+ .BuildServiceProvider();
+
+ _operationFactory = serviceProvider.GetService();
+ _mapper = serviceProvider.GetService();
+ }
+
+ private async Task SetAccountInfo(string accountInfoFileName, AccountInformationPipelineContextData contextData)
+ {
+ WotAccountInfo accountInfo = null;
+ using (var r = new StreamReader(accountInfoFileName))
+ {
+ var json = await r.ReadToEndAsync();
+ var account = JsonConvert.DeserializeObject>>(json);
+ accountInfo = account.Data?.Values.FirstOrDefault();
+ }
+
+ if (accountInfo == null)
+ {
+ throw new ApplicationException($"Can not read account info from '{accountInfoFileName}'");
+ }
+
+ // Store info to AccountInfo and in AccountInfoHistory
+ contextData.AccountInfo = _mapper.Map(accountInfo);
+ contextData.AccountInfo.Realm = Realm;
+
+ contextData.AccountInfoHistory = new AccountInfoHistory(contextData.AccountInfo.AccountId, contextData.AccountInfo.LastBattleTime);
+ _mapper.Map(accountInfo.Statistics?.All, contextData.AccountInfoHistory);
+ }
+
+ private async Task SetTanksInfo(string tanksInfoFileName, AccountInformationPipelineContextData contextData)
+ {
+ List tanksInfo = null;
+
+ using (var r = new StreamReader(tanksInfoFileName))
+ {
+ var json = await r.ReadToEndAsync();
+ var tanks = JsonConvert.DeserializeObject>>>(json);
+ tanksInfo = tanks.Data?.Values.FirstOrDefault();
+ }
+
+
+ if (tanksInfo == null)
+ {
+ throw new ApplicationException($"Can not read tanks info from '{tanksInfoFileName}'");
+ }
+
+ contextData.Tanks = new List();
+ contextData.TanksHistory = new Dictionary();
+
+ tanksInfo.OrderByDescending(t => t.LastBattleTime).ToList().ForEach(tank =>
+ {
+ var tankInfo = new TankInfo(tank.AccountId, tank.TankId);
+ _mapper.Map(tank, tankInfo);
+ contextData.Tanks.Add(tankInfo);
+ var stat = new TankInfoHistory(tank.AccountId, tank.TankId, tank.LastBattleTime);
+ _mapper.Map(tank.All, stat);
+ contextData.TanksHistory[stat.TankId] = stat;
+ });
+
+ }
+
+
+ }
+}
\ No newline at end of file
diff --git a/DataImporterTool/Importer/MongoSettings.cs b/DataImporterTool/Importer/MongoSettings.cs
new file mode 100644
index 0000000..48101f0
--- /dev/null
+++ b/DataImporterTool/Importer/MongoSettings.cs
@@ -0,0 +1,10 @@
+using WotBlitzStatisticsPro.Common;
+
+namespace DataImporterTool.Importer
+{
+ public class MongoSettings : IMongoSettings
+ {
+ public string ConnectionString { get; set; }
+ public string DatabaseName { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/DataImporterTool/MainForm.Designer.cs b/DataImporterTool/MainForm.Designer.cs
new file mode 100644
index 0000000..d09d2a6
--- /dev/null
+++ b/DataImporterTool/MainForm.Designer.cs
@@ -0,0 +1,336 @@
+
+namespace DataImporterTool
+{
+ partial class MainForm
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.panel1 = new System.Windows.Forms.Panel();
+ this.txtMongoConnectionString = new System.Windows.Forms.TextBox();
+ this.label1 = new System.Windows.Forms.Label();
+ this.tabImportType = new System.Windows.Forms.TabControl();
+ this.tabPgFileImport = new System.Windows.Forms.TabPage();
+ this.btnStartImport = new System.Windows.Forms.Button();
+ this.lstTanks = new System.Windows.Forms.ListBox();
+ this.lstAccounts = new System.Windows.Forms.ListBox();
+ this.label3 = new System.Windows.Forms.Label();
+ this.label2 = new System.Windows.Forms.Label();
+ this.btnAddAsTanks = new System.Windows.Forms.Button();
+ this.btnAddAsAccounts = new System.Windows.Forms.Button();
+ this.lstAllFiles = new System.Windows.Forms.ListBox();
+ this.grpFolder = new System.Windows.Forms.GroupBox();
+ this.btnOpenFolder = new System.Windows.Forms.Button();
+ this.txtJsonFolder = new System.Windows.Forms.TextBox();
+ this.tabPgSqlImport = new System.Windows.Forms.TabPage();
+ this.statusStrip = new System.Windows.Forms.StatusStrip();
+ this.statusImportLabel = new System.Windows.Forms.ToolStripStatusLabel();
+ this.statusImportProgressBar = new System.Windows.Forms.ToolStripProgressBar();
+ this.txtIpmortLog = new System.Windows.Forms.TextBox();
+ this.textBox1 = new System.Windows.Forms.TextBox();
+ this.panel1.SuspendLayout();
+ this.tabImportType.SuspendLayout();
+ this.tabPgFileImport.SuspendLayout();
+ this.grpFolder.SuspendLayout();
+ this.statusStrip.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // panel1
+ //
+ this.panel1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
+ this.panel1.Controls.Add(this.txtMongoConnectionString);
+ this.panel1.Controls.Add(this.label1);
+ this.panel1.Dock = System.Windows.Forms.DockStyle.Top;
+ this.panel1.Location = new System.Drawing.Point(0, 0);
+ this.panel1.Name = "panel1";
+ this.panel1.Size = new System.Drawing.Size(833, 42);
+ this.panel1.TabIndex = 0;
+ //
+ // txtMongoConnectionString
+ //
+ this.txtMongoConnectionString.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.txtMongoConnectionString.Location = new System.Drawing.Point(179, 10);
+ this.txtMongoConnectionString.Name = "txtMongoConnectionString";
+ this.txtMongoConnectionString.Size = new System.Drawing.Size(641, 23);
+ this.txtMongoConnectionString.TabIndex = 1;
+ //
+ // label1
+ //
+ this.label1.AutoSize = true;
+ this.label1.Location = new System.Drawing.Point(13, 13);
+ this.label1.Name = "label1";
+ this.label1.Size = new System.Drawing.Size(160, 15);
+ this.label1.TabIndex = 0;
+ this.label1.Text = "MongoDB connection string:";
+ //
+ // tabImportType
+ //
+ this.tabImportType.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.tabImportType.Controls.Add(this.tabPgFileImport);
+ this.tabImportType.Controls.Add(this.tabPgSqlImport);
+ this.tabImportType.Location = new System.Drawing.Point(0, 48);
+ this.tabImportType.Name = "tabImportType";
+ this.tabImportType.SelectedIndex = 0;
+ this.tabImportType.Size = new System.Drawing.Size(833, 275);
+ this.tabImportType.TabIndex = 1;
+ //
+ // tabPgFileImport
+ //
+ this.tabPgFileImport.Controls.Add(this.btnStartImport);
+ this.tabPgFileImport.Controls.Add(this.lstTanks);
+ this.tabPgFileImport.Controls.Add(this.lstAccounts);
+ this.tabPgFileImport.Controls.Add(this.label3);
+ this.tabPgFileImport.Controls.Add(this.label2);
+ this.tabPgFileImport.Controls.Add(this.btnAddAsTanks);
+ this.tabPgFileImport.Controls.Add(this.btnAddAsAccounts);
+ this.tabPgFileImport.Controls.Add(this.lstAllFiles);
+ this.tabPgFileImport.Controls.Add(this.grpFolder);
+ this.tabPgFileImport.Location = new System.Drawing.Point(4, 24);
+ this.tabPgFileImport.Name = "tabPgFileImport";
+ this.tabPgFileImport.Padding = new System.Windows.Forms.Padding(3);
+ this.tabPgFileImport.Size = new System.Drawing.Size(825, 247);
+ this.tabPgFileImport.TabIndex = 0;
+ this.tabPgFileImport.Text = "Import from Json files";
+ this.tabPgFileImport.UseVisualStyleBackColor = true;
+ //
+ // btnStartImport
+ //
+ this.btnStartImport.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.btnStartImport.Location = new System.Drawing.Point(306, 180);
+ this.btnStartImport.Name = "btnStartImport";
+ this.btnStartImport.Size = new System.Drawing.Size(87, 48);
+ this.btnStartImport.TabIndex = 7;
+ this.btnStartImport.Text = "Start import!";
+ this.btnStartImport.UseVisualStyleBackColor = true;
+ //
+ // lstTanks
+ //
+ this.lstTanks.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.lstTanks.FormattingEnabled = true;
+ this.lstTanks.ItemHeight = 15;
+ this.lstTanks.Location = new System.Drawing.Point(616, 74);
+ this.lstTanks.Name = "lstTanks";
+ this.lstTanks.Size = new System.Drawing.Size(200, 154);
+ this.lstTanks.TabIndex = 6;
+ //
+ // lstAccounts
+ //
+ this.lstAccounts.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.lstAccounts.FormattingEnabled = true;
+ this.lstAccounts.ItemHeight = 15;
+ this.lstAccounts.Location = new System.Drawing.Point(399, 75);
+ this.lstAccounts.Name = "lstAccounts";
+ this.lstAccounts.Size = new System.Drawing.Size(200, 154);
+ this.lstAccounts.TabIndex = 5;
+ //
+ // label3
+ //
+ this.label3.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.label3.AutoSize = true;
+ this.label3.Location = new System.Drawing.Point(616, 53);
+ this.label3.Name = "label3";
+ this.label3.Size = new System.Drawing.Size(39, 15);
+ this.label3.TabIndex = 4;
+ this.label3.Text = "Tanks:";
+ //
+ // label2
+ //
+ this.label2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.label2.AutoSize = true;
+ this.label2.Location = new System.Drawing.Point(399, 53);
+ this.label2.Name = "label2";
+ this.label2.Size = new System.Drawing.Size(60, 15);
+ this.label2.TabIndex = 4;
+ this.label2.Text = "Accounts:";
+ //
+ // btnAddAsTanks
+ //
+ this.btnAddAsTanks.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.btnAddAsTanks.Location = new System.Drawing.Point(305, 105);
+ this.btnAddAsTanks.Name = "btnAddAsTanks";
+ this.btnAddAsTanks.Size = new System.Drawing.Size(88, 23);
+ this.btnAddAsTanks.TabIndex = 3;
+ this.btnAddAsTanks.Text = "Tanks >";
+ this.btnAddAsTanks.UseVisualStyleBackColor = true;
+ //
+ // btnAddAsAccounts
+ //
+ this.btnAddAsAccounts.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.btnAddAsAccounts.Location = new System.Drawing.Point(305, 75);
+ this.btnAddAsAccounts.Name = "btnAddAsAccounts";
+ this.btnAddAsAccounts.Size = new System.Drawing.Size(88, 23);
+ this.btnAddAsAccounts.TabIndex = 2;
+ this.btnAddAsAccounts.Text = "Accounts >";
+ this.btnAddAsAccounts.UseVisualStyleBackColor = true;
+ //
+ // lstAllFiles
+ //
+ this.lstAllFiles.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.lstAllFiles.FormattingEnabled = true;
+ this.lstAllFiles.ItemHeight = 15;
+ this.lstAllFiles.Location = new System.Drawing.Point(10, 56);
+ this.lstAllFiles.Name = "lstAllFiles";
+ this.lstAllFiles.SelectionMode = System.Windows.Forms.SelectionMode.MultiExtended;
+ this.lstAllFiles.Size = new System.Drawing.Size(289, 184);
+ this.lstAllFiles.TabIndex = 1;
+ //
+ // grpFolder
+ //
+ this.grpFolder.Controls.Add(this.btnOpenFolder);
+ this.grpFolder.Controls.Add(this.txtJsonFolder);
+ this.grpFolder.Dock = System.Windows.Forms.DockStyle.Top;
+ this.grpFolder.Location = new System.Drawing.Point(3, 3);
+ this.grpFolder.Name = "grpFolder";
+ this.grpFolder.Size = new System.Drawing.Size(819, 47);
+ this.grpFolder.TabIndex = 0;
+ this.grpFolder.TabStop = false;
+ this.grpFolder.Text = "Json folder";
+ //
+ // btnOpenFolder
+ //
+ this.btnOpenFolder.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.btnOpenFolder.Location = new System.Drawing.Point(782, 18);
+ this.btnOpenFolder.Name = "btnOpenFolder";
+ this.btnOpenFolder.Size = new System.Drawing.Size(31, 23);
+ this.btnOpenFolder.TabIndex = 1;
+ this.btnOpenFolder.Text = "...";
+ this.btnOpenFolder.UseVisualStyleBackColor = true;
+ //
+ // txtJsonFolder
+ //
+ this.txtJsonFolder.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.txtJsonFolder.Location = new System.Drawing.Point(7, 18);
+ this.txtJsonFolder.Name = "txtJsonFolder";
+ this.txtJsonFolder.Size = new System.Drawing.Size(768, 23);
+ this.txtJsonFolder.TabIndex = 0;
+ //
+ // tabPgSqlImport
+ //
+ this.tabPgSqlImport.Location = new System.Drawing.Point(4, 24);
+ this.tabPgSqlImport.Name = "tabPgSqlImport";
+ this.tabPgSqlImport.Padding = new System.Windows.Forms.Padding(3);
+ this.tabPgSqlImport.Size = new System.Drawing.Size(825, 247);
+ this.tabPgSqlImport.TabIndex = 1;
+ this.tabPgSqlImport.Text = "Import from Sql database";
+ this.tabPgSqlImport.UseVisualStyleBackColor = true;
+ //
+ // statusStrip
+ //
+ this.statusStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
+ this.statusImportLabel,
+ this.statusImportProgressBar});
+ this.statusStrip.Location = new System.Drawing.Point(0, 502);
+ this.statusStrip.Name = "statusStrip";
+ this.statusStrip.Size = new System.Drawing.Size(833, 22);
+ this.statusStrip.TabIndex = 2;
+ this.statusStrip.Text = "ImportSatatusStrip";
+ //
+ // statusImportLabel
+ //
+ this.statusImportLabel.Name = "statusImportLabel";
+ this.statusImportLabel.Size = new System.Drawing.Size(0, 17);
+ //
+ // statusImportProgressBar
+ //
+ this.statusImportProgressBar.Name = "statusImportProgressBar";
+ this.statusImportProgressBar.Size = new System.Drawing.Size(100, 16);
+ //
+ // txtIpmortLog
+ //
+ this.txtIpmortLog.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.txtIpmortLog.Location = new System.Drawing.Point(4, 329);
+ this.txtIpmortLog.Multiline = true;
+ this.txtIpmortLog.Name = "txtIpmortLog";
+ this.txtIpmortLog.ReadOnly = true;
+ this.txtIpmortLog.Size = new System.Drawing.Size(825, 170);
+ this.txtIpmortLog.TabIndex = 3;
+ //
+ // textBox1
+ //
+ this.textBox1.Location = new System.Drawing.Point(0, 0);
+ this.textBox1.Name = "textBox1";
+ this.textBox1.Size = new System.Drawing.Size(100, 23);
+ this.textBox1.TabIndex = 0;
+ this.textBox1.Text = "textBox1";
+ //
+ // MainForm
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.ClientSize = new System.Drawing.Size(833, 524);
+ this.Controls.Add(this.txtIpmortLog);
+ this.Controls.Add(this.statusStrip);
+ this.Controls.Add(this.tabImportType);
+ this.Controls.Add(this.panel1);
+ this.Name = "MainForm";
+ this.Text = "Wot blitz statististics data importer";
+ this.panel1.ResumeLayout(false);
+ this.panel1.PerformLayout();
+ this.tabImportType.ResumeLayout(false);
+ this.tabPgFileImport.ResumeLayout(false);
+ this.tabPgFileImport.PerformLayout();
+ this.grpFolder.ResumeLayout(false);
+ this.grpFolder.PerformLayout();
+ this.statusStrip.ResumeLayout(false);
+ this.statusStrip.PerformLayout();
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.Panel panel1;
+ private System.Windows.Forms.Label label1;
+ private System.Windows.Forms.TextBox txtMongoConnectionString;
+ private System.Windows.Forms.TabControl tabImportType;
+ private System.Windows.Forms.TabPage tabPgSqlImport;
+ private System.Windows.Forms.TabPage tabPgFileImport;
+ private System.Windows.Forms.StatusStrip statusStrip;
+ private System.Windows.Forms.ToolStripStatusLabel statusImportLabel;
+ private System.Windows.Forms.ToolStripProgressBar statusImportProgressBar;
+ private System.Windows.Forms.TextBox txtIpmortLog;
+ private System.Windows.Forms.TextBox textBox1;
+ private System.Windows.Forms.GroupBox grpFolder;
+ private System.Windows.Forms.Button btnOpenFolder;
+ private System.Windows.Forms.TextBox txtJsonFolder;
+ private System.Windows.Forms.Label label3;
+ private System.Windows.Forms.Label label2;
+ private System.Windows.Forms.Button btnAddAsTanks;
+ private System.Windows.Forms.Button btnAddAsAccounts;
+ private System.Windows.Forms.ListBox lstAllFiles;
+ private System.Windows.Forms.ListBox lstTanks;
+ private System.Windows.Forms.ListBox lstAccounts;
+ private System.Windows.Forms.Button btnStartImport;
+ }
+}
+
diff --git a/DataImporterTool/MainForm.cs b/DataImporterTool/MainForm.cs
new file mode 100644
index 0000000..95b8814
--- /dev/null
+++ b/DataImporterTool/MainForm.cs
@@ -0,0 +1,106 @@
+using System;
+using System.Linq;
+using System.Windows.Forms;
+
+namespace DataImporterTool
+{
+ public partial class MainForm : Form, IMainFormView
+ {
+ public MainForm()
+ {
+ InitializeComponent();
+
+ statusImportProgressBar.Minimum = 0;
+ statusImportProgressBar.Maximum = 100;
+
+ btnOpenFolder.Click += BtnOpenFolder_Click;
+ btnAddAsAccounts.Click += BtnAddAsAccounts_Click;
+ btnAddAsTanks.Click += BtnAddAsTanks_Click;
+ btnStartImport.Click += BtnStartImport_Click;
+ }
+
+ public string MongoDbConnectionString
+ {
+ get => txtMongoConnectionString.Text;
+ set => txtMongoConnectionString.Text = value;
+ }
+
+ public string JsonFolderPath
+ {
+ get => txtJsonFolder.Text;
+ set => txtJsonFolder.Text = value;
+ }
+
+ public string StatusInformation
+ {
+ get => statusImportLabel.Text;
+ set => statusImportLabel.Text = value;
+ }
+
+ public event Action OpenJsonFolderClick;
+
+ public event Action AddFilesAsAccountsInfoList;
+
+ public event Action AddFilesAsTanksInfoList;
+
+ public event Action StartJsonConvert;
+
+ public void ShowJsonFiles(string[] allJsonFiles)
+ {
+ lstAllFiles.DataSource = allJsonFiles;
+ }
+
+ public void ShowJsonAccountFiles(string[] accountJsonFiles)
+ {
+ lstAccounts.DataSource = accountJsonFiles;
+ }
+
+ public void ShowJsonTankFiles(string[] tankJsonFiles)
+ {
+ lstTanks.DataSource = tankJsonFiles;
+ }
+
+ public void ClearLog()
+ {
+ txtIpmortLog.Text = string.Empty;
+ }
+
+ public void AppendLogRow(string logInfo)
+ {
+ txtIpmortLog.Text = $"{txtIpmortLog.Text}{Environment.NewLine}{logInfo}";
+ }
+
+ public void SetProcessPercentage(int percentage)
+ {
+ statusImportProgressBar.Value = percentage;
+ }
+
+ private void BtnOpenFolder_Click(object sender, EventArgs e)
+ {
+ OpenJsonFolderClick?.Invoke();
+ }
+
+ private void BtnAddAsAccounts_Click(object sender, EventArgs e)
+ {
+ var selected = lstAllFiles.SelectedItems.Cast().ToArray();
+ if (selected.Length > 0)
+ {
+ AddFilesAsAccountsInfoList?.Invoke(selected);
+ }
+ }
+
+ private void BtnAddAsTanks_Click(object sender, EventArgs e)
+ {
+ var selected = lstAllFiles.SelectedItems.Cast().ToArray();
+ if (selected.Length > 0)
+ {
+ AddFilesAsTanksInfoList?.Invoke(selected);
+ }
+ }
+
+ private void BtnStartImport_Click(object sender, EventArgs e)
+ {
+ StartJsonConvert?.Invoke();
+ }
+ }
+}
diff --git a/DataImporterTool/MainForm.resx b/DataImporterTool/MainForm.resx
new file mode 100644
index 0000000..f298a7b
--- /dev/null
+++ b/DataImporterTool/MainForm.resx
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/DataImporterTool/MainFormPresenter.cs b/DataImporterTool/MainFormPresenter.cs
new file mode 100644
index 0000000..0cf0c6e
--- /dev/null
+++ b/DataImporterTool/MainFormPresenter.cs
@@ -0,0 +1,118 @@
+using System;
+using System.IO;
+using System.Linq;
+using System.Text;
+using DataImporterTool.Dialogs;
+using DataImporterTool.Importer;
+
+namespace DataImporterTool
+{
+ public class MainFormPresenter
+ {
+ private readonly IDialogService _dialogService;
+ private readonly JsonFilesImporter _jsonFilesImporter;
+
+ private string[] _selectedFileAsAccounts = null;
+ private string[] _selectedFileAsTanks = null;
+
+ public MainFormPresenter(
+ IMainFormView view,
+ IDialogService dialogService,
+ JsonFilesImporter jsonFilesImporter)
+ {
+ _dialogService = dialogService;
+ _jsonFilesImporter = jsonFilesImporter;
+ View = view;
+ View.OpenJsonFolderClick += OpenJsonFolder;
+ View.AddFilesAsAccountsInfoList += AddFilesAsAccountsInfoList;
+ View.AddFilesAsTanksInfoList += AddFilesAsTanksInfoList;
+ View.StartJsonConvert += StartJsonConvert;
+ }
+
+ public IMainFormView View { get; }
+
+ private void OpenJsonFolder()
+ {
+ View.JsonFolderPath = _dialogService.ChooseFolder() ?? string.Empty;
+ if (!string.IsNullOrEmpty(View.JsonFolderPath))
+ {
+ var files = Directory.GetFiles(View.JsonFolderPath, "*.json").Select(Path.GetFileName).ToArray();
+ View.ShowJsonFiles(files);
+ }
+
+ }
+
+ private void AddFilesAsAccountsInfoList(string[] files)
+ {
+ _selectedFileAsAccounts = files;
+ View.ShowJsonAccountFiles(files);
+ UpdateStatusInformation();
+ }
+
+ private void AddFilesAsTanksInfoList(string[] files)
+ {
+ _selectedFileAsTanks = files;
+ View.ShowJsonTankFiles(files);
+ UpdateStatusInformation();
+ }
+
+
+ private void UpdateStatusInformation()
+ {
+ var text = new StringBuilder();
+ if (_selectedFileAsAccounts != null)
+ {
+ text.Append($"{_selectedFileAsAccounts.Length} accounts json files selected");
+ }
+
+ if (_selectedFileAsTanks != null)
+ {
+ text.Append($"; {_selectedFileAsTanks.Length} tanks json files selected");
+ }
+
+ View.StatusInformation = text.ToString();
+ }
+
+ private async void StartJsonConvert()
+ {
+ if (string.IsNullOrEmpty(View.MongoDbConnectionString))
+ {
+ _dialogService.ShowWarning("MongoDB connection string is empty!");
+ return;
+ }
+
+ if (_selectedFileAsAccounts == null || _selectedFileAsAccounts.Length == 0
+ || _selectedFileAsTanks == null || _selectedFileAsTanks.Length == 0)
+ {
+ _dialogService.ShowWarning("Should be selected some account and tank json files");
+ return;
+ }
+
+ if (_selectedFileAsTanks != null && _selectedFileAsAccounts != null &&
+ _selectedFileAsAccounts.Length != _selectedFileAsTanks.Length)
+ {
+ _dialogService.ShowWarning("Account files count should be equal to tank files count");
+ return;
+ }
+
+ View.ClearLog();
+ View.SetProcessPercentage(0);
+
+ var progress = new Progress();
+ progress.ProgressChanged += ProgressChanged;
+
+ await _jsonFilesImporter.Import(View.MongoDbConnectionString,
+ View.JsonFolderPath,
+ _selectedFileAsAccounts,
+ _selectedFileAsTanks,
+ progress);
+ }
+
+ private void ProgressChanged(object sender, ImportProgress e)
+ {
+ View.StatusInformation = $"{e.FilesConverted} files converted ({e.Progress}%)";
+ View.AppendLogRow($"Converted: '{e.AccountFileConverted}' - '{e.TankFileConverted}': '{e.AccountName}', {e.BattlesCount} battles, '{e.LastBattle.ToShortDateString()}', {e.TanksCount} tanks");
+ View.SetProcessPercentage(e.Progress);
+ }
+ }
+}
\ No newline at end of file
diff --git a/DataImporterTool/Program.cs b/DataImporterTool/Program.cs
new file mode 100644
index 0000000..1a863b5
--- /dev/null
+++ b/DataImporterTool/Program.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Windows.Forms;
+using DataImporterTool.Dialogs;
+using DataImporterTool.Importer;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace DataImporterTool
+{
+ static class Program
+ {
+ ///
+ /// The main entry point for the application.
+ ///
+ [STAThread]
+ static void Main()
+ {
+ Application.SetHighDpiMode(HighDpiMode.SystemAware);
+ Application.EnableVisualStyles();
+ Application.SetCompatibleTextRenderingDefault(false);
+
+ var serviceProvider = new ServiceCollection()
+ .AddSingleton()
+ .AddSingleton()
+ .AddSingleton()
+ .AddSingleton()
+ .BuildServiceProvider();
+
+ var mainFormPresenter = serviceProvider.GetService();
+
+ var form = mainFormPresenter.View as Form;
+
+ Application.Run(form);
+ }
+
+ }
+}
diff --git a/WotBlitzStatisticsPro.Tests/DictionariesTests/AchievementsDictionaryUpdaterTests.cs b/WotBlitzStatisticsPro.Tests/DictionariesTests/AchievementsDictionaryUpdaterTests.cs
index 8df79e1..4a10118 100644
--- a/WotBlitzStatisticsPro.Tests/DictionariesTests/AchievementsDictionaryUpdaterTests.cs
+++ b/WotBlitzStatisticsPro.Tests/DictionariesTests/AchievementsDictionaryUpdaterTests.cs
@@ -115,7 +115,7 @@ namespace WotBlitzStatisticsPro.Tests.DictionariesTests
if (_achievementsInfoResponseEn[i].Options == null)
{
- targetAchievementDictionaries[i].Options.Should().BeNull();
+ targetAchievementDictionaries[i].Options.Should().BeEmpty();
}
else
{
diff --git a/WotBlitzStatisticsPro.WgApiClient/ResponseBody.cs b/WotBlitzStatisticsPro.WgApiClient/ResponseBody.cs
index 4599ae7..db7d612 100644
--- a/WotBlitzStatisticsPro.WgApiClient/ResponseBody.cs
+++ b/WotBlitzStatisticsPro.WgApiClient/ResponseBody.cs
@@ -1,10 +1,8 @@
-using System.ComponentModel.DataAnnotations;
-using System.Diagnostics.CodeAnalysis;
-using Newtonsoft.Json;
+using Newtonsoft.Json;
namespace WotBlitzStatisticsPro.WgApiClient
{
- internal class ResponseBody where T: class
+ public class ResponseBody where T: class
{
[JsonProperty("status")]
public string? Status { get; set; }
@@ -19,13 +17,13 @@ namespace WotBlitzStatisticsPro.WgApiClient
public Error? Error { get; set; }
}
- internal class Meta
+ public class Meta
{
[JsonProperty("count")]
public int Count { get; set; }
}
- internal class Error
+ public class Error
{
[JsonProperty("field")]
public string? Field { get; set; }
diff --git a/WotBlitzStatisticsPro.sln b/WotBlitzStatisticsPro.sln
index 9d825d0..b66ff38 100644
--- a/WotBlitzStatisticsPro.sln
+++ b/WotBlitzStatisticsPro.sln
@@ -13,9 +13,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WotBlitzStatisticsPro.Logic
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WotBlitzStatisticsPro.GraphQl", "WotBlitzStatisticsPro.GraphQl\WotBlitzStatisticsPro.GraphQl.csproj", "{921E0CBA-5696-4BB3-A547-78F599B1479B}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WotBlitzStatisticsPro.DataAccess", "WotBlitzStatisticsPro.DataAccess\WotBlitzStatisticsPro.DataAccess.csproj", "{19B1EDE4-1B83-49E2-A666-6CFE3A4A732E}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WotBlitzStatisticsPro.DataAccess", "WotBlitzStatisticsPro.DataAccess\WotBlitzStatisticsPro.DataAccess.csproj", "{19B1EDE4-1B83-49E2-A666-6CFE3A4A732E}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WotBlitzStatisticsPro.Tests", "WotBlitzStatisticsPro.Tests\WotBlitzStatisticsPro.Tests.csproj", "{551C3088-C6EA-45E1-A665-7C924C8407EB}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WotBlitzStatisticsPro.Tests", "WotBlitzStatisticsPro.Tests\WotBlitzStatisticsPro.Tests.csproj", "{551C3088-C6EA-45E1-A665-7C924C8407EB}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataImporterTool", "DataImporterTool\DataImporterTool.csproj", "{8C30F990-ACCE-401E-9C5F-F6EFA198F8D0}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -47,6 +49,10 @@ Global
{551C3088-C6EA-45E1-A665-7C924C8407EB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{551C3088-C6EA-45E1-A665-7C924C8407EB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{551C3088-C6EA-45E1-A665-7C924C8407EB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8C30F990-ACCE-401E-9C5F-F6EFA198F8D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8C30F990-ACCE-401E-9C5F-F6EFA198F8D0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8C30F990-ACCE-401E-9C5F-F6EFA198F8D0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8C30F990-ACCE-401E-9C5F-F6EFA198F8D0}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -56,6 +62,7 @@ Global
{A79103B3-4F04-4090-803E-B7AABFCC93B1} = {5095AE8F-F1D4-4BC0-95FD-B3DB0D3AED15}
{19B1EDE4-1B83-49E2-A666-6CFE3A4A732E} = {5095AE8F-F1D4-4BC0-95FD-B3DB0D3AED15}
{551C3088-C6EA-45E1-A665-7C924C8407EB} = {5095AE8F-F1D4-4BC0-95FD-B3DB0D3AED15}
+ {8C30F990-ACCE-401E-9C5F-F6EFA198F8D0} = {5095AE8F-F1D4-4BC0-95FD-B3DB0D3AED15}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {295179CC-53B9-4A2F-8051-A6B033EED16E}