From 26e469d69da721f2b77e70fa824112b322abcf36 Mon Sep 17 00:00:00 2001 From: Mikhail Shabanov Date: Tue, 29 Dec 2020 19:23:02 +0100 Subject: [PATCH] Json importer ready --- DataImporterTool/DataImporterTool.csproj | 23 ++ DataImporterTool/Dialogs/DialogService.cs | 27 ++ DataImporterTool/Dialogs/IDialogService.cs | 10 + DataImporterTool/IMainFormView.cs | 33 ++ DataImporterTool/Importer/ImportProgress.cs | 23 ++ .../Importer/JsonFilesImporter.cs | 175 +++++++++ DataImporterTool/Importer/MongoSettings.cs | 10 + DataImporterTool/MainForm.Designer.cs | 336 ++++++++++++++++++ DataImporterTool/MainForm.cs | 106 ++++++ DataImporterTool/MainForm.resx | 60 ++++ DataImporterTool/MainFormPresenter.cs | 118 ++++++ DataImporterTool/Program.cs | 36 ++ .../AchievementsDictionaryUpdaterTests.cs | 2 +- .../ResponseBody.cs | 10 +- WotBlitzStatisticsPro.sln | 11 +- 15 files changed, 971 insertions(+), 9 deletions(-) create mode 100644 DataImporterTool/DataImporterTool.csproj create mode 100644 DataImporterTool/Dialogs/DialogService.cs create mode 100644 DataImporterTool/Dialogs/IDialogService.cs create mode 100644 DataImporterTool/IMainFormView.cs create mode 100644 DataImporterTool/Importer/ImportProgress.cs create mode 100644 DataImporterTool/Importer/JsonFilesImporter.cs create mode 100644 DataImporterTool/Importer/MongoSettings.cs create mode 100644 DataImporterTool/MainForm.Designer.cs create mode 100644 DataImporterTool/MainForm.cs create mode 100644 DataImporterTool/MainForm.resx create mode 100644 DataImporterTool/MainFormPresenter.cs create mode 100644 DataImporterTool/Program.cs 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}