mirror of
https://github.com/DrMboga/WotBlitzStatisticsBlazor.git
synced 2026-10-21 10:27:18 +02:00
425 lines
19 KiB
C#
425 lines
19 KiB
C#
#nullable enable
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Data;
|
|
using System.Data.Common;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using AutoMapper;
|
|
using Microsoft.Data.SqlClient;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Microsoft.Extensions.Logging;
|
|
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.Calculations;
|
|
using WotBlitzStatisticsPro.Logic.Pipeline;
|
|
|
|
namespace DataImporterTool.Importer
|
|
{
|
|
public class SqlImporter
|
|
{
|
|
private IOperationFactory _operationFactory = null;
|
|
private IMapper _mapper = null;
|
|
|
|
public async Task<Dictionary<long, string>?> FetchAllAccounts(string connectionString)
|
|
{
|
|
using (var context = await CreateDbContext(connectionString))
|
|
{
|
|
var accounts = await context.AccountInfos.FromSqlRaw(FetchAllAccountsQuery).ToListAsync();
|
|
if (accounts == null || accounts.Count == 0)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return accounts.ToDictionary(a => a.AccountId, a => a.Nickname);
|
|
}
|
|
}
|
|
|
|
public async Task StartImport(
|
|
string mongoConnectionString,
|
|
string sqlConnectionString,
|
|
long[] accountsToImport,
|
|
IProgress<ImportProgress> progress)
|
|
{
|
|
// Get summary history count - for percentage calculation
|
|
// Here I'm using simple query in the loop. But there is the way to pass an array as SQL parameter - using DataTable
|
|
int historyCount = 0;
|
|
using (var context = await CreateDbContext(sqlConnectionString))
|
|
{
|
|
var connection = context.Database.GetDbConnection();
|
|
foreach (long accountId in accountsToImport)
|
|
{
|
|
historyCount += await GetAccountHistoryCount(accountId, connection);
|
|
}
|
|
if (connection.State.Equals(ConnectionState.Open)) { await connection.CloseAsync(); }
|
|
}
|
|
|
|
var allOldData = new AccountAndTanksContextRow[historyCount];
|
|
await GetAllDataFromOldDatabase(sqlConnectionString, accountsToImport, progress, allOldData, historyCount);
|
|
|
|
SetupOperations(mongoConnectionString);
|
|
|
|
int historyCounter = historyCount;
|
|
for (int i = 0; i < historyCount; i++)
|
|
{
|
|
var saved = await SaveAccountAndTankInformation(allOldData[i]);
|
|
|
|
var p = new ImportProgress
|
|
{
|
|
Progress = 100 * (historyCounter + 1) / (2 * historyCount),
|
|
AccountName = saved.Nickname,
|
|
LastBattle = saved.LastBattleTime,
|
|
BattlesCount = saved.Battles,
|
|
TanksCount = saved.Tanks?.Count ?? 0
|
|
};
|
|
progress.Report(p);
|
|
historyCounter++;
|
|
}
|
|
}
|
|
|
|
private async Task<AccountInfoResponse> SaveAccountAndTankInformation(AccountAndTanksContextRow accountAndTanksInfo)
|
|
{
|
|
var contextData = new AccountInformationPipelineContextData
|
|
{
|
|
AccountInfo = accountAndTanksInfo.AccountInfo,
|
|
AccountInfoHistory = accountAndTanksInfo.AccountInfoHistory,
|
|
Tanks = accountAndTanksInfo.Tanks,
|
|
TanksHistory = accountAndTanksInfo.TanksHistory
|
|
};
|
|
|
|
var context = new OperationContext(new AccountRequest(contextData.AccountInfo?.AccountId ?? -1, RealmType.Ru, RequestLanguage.En));
|
|
context.AddOrReplace(contextData);
|
|
var pipeline = new Pipeline<IOperationContext>(_operationFactory);
|
|
|
|
pipeline
|
|
.AddOperation<CalculateStatisticsOperation>()
|
|
.AddOperation<SaveAccountAndTanksOperation>()
|
|
.AddOperation<BuildAccountInfoResponseOperation>()
|
|
;
|
|
|
|
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<IMongoSettings>(settings)
|
|
.AddTransient<IDictionariesDataAccessor, DictionariesDataAccessor>()
|
|
.AddTransient<IWargamingAccountDataAccessor, WargamingAccountDataAccessor>()
|
|
.AddTransient<CalculateStatisticsOperation>()
|
|
.AddTransient<BuildAccountInfoResponseOperation>()
|
|
.AddTransient<SaveAccountAndTanksOperation>()
|
|
.AddTransient<IOperationFactory>(sp =>
|
|
new ServiceProviderOperationFactory(sp))
|
|
.BuildServiceProvider();
|
|
|
|
_operationFactory = serviceProvider.GetService<IOperationFactory>();
|
|
_mapper = serviceProvider.GetService<IMapper>();
|
|
}
|
|
|
|
|
|
#region SQL Database stuff
|
|
private async Task GetAllDataFromOldDatabase(string sqlConnectionString, long[] accountsToImport, IProgress<ImportProgress> progress,
|
|
AccountAndTanksContextRow[] allOldData, int historyCount)
|
|
{
|
|
int historyCounter = 0;
|
|
|
|
// In loop, get account info, history rows and tank history rows and collect to List
|
|
using (var context = await CreateDbContext(sqlConnectionString))
|
|
{
|
|
var connection = context.Database.GetDbConnection();
|
|
|
|
foreach (long accountId in accountsToImport)
|
|
{
|
|
var accountInfo = await context.AccountInfos.FromSqlRaw(
|
|
SelectAccountInfoQuery,
|
|
new SqlParameter("AccountId", SqlDbType.Int) {Value = accountId}).FirstOrDefaultAsync();
|
|
|
|
var allHistory = await GetAccountInfoHistory(accountInfo.AccountId, connection);
|
|
foreach (var accountInfoHistory in allHistory)
|
|
{
|
|
allOldData[historyCounter] = new AccountAndTanksContextRow
|
|
{
|
|
AccountInfo = new AccountInfo
|
|
{
|
|
AccountId = accountId,
|
|
CreatedAt = accountInfo.CreatedAt,
|
|
LastBattleTime = accountInfoHistory.AccountInfoHistory.LastBattleTime,
|
|
Nickname = accountInfo.Nickname,
|
|
Realm = RealmType.Ru,
|
|
UpdatedAt = accountInfoHistory.UpdatedAt
|
|
},
|
|
AccountInfoHistory = accountInfoHistory.AccountInfoHistory,
|
|
Tanks = new List<TankInfo>(),
|
|
TanksHistory = new Dictionary<long, TankInfoHistory>()
|
|
};
|
|
|
|
var oldTanksListByUpdateDate = await GetTanksHistory(accountId,
|
|
allOldData[historyCounter].AccountInfo.LastBattleTime, connection);
|
|
|
|
foreach (var tankInfo in oldTanksListByUpdateDate)
|
|
{
|
|
allOldData[historyCounter].Tanks.Add(new TankInfo(accountId, tankInfo.history.TankId)
|
|
{
|
|
LastBattleTime = tankInfo.history.LastBattleTime,
|
|
BattleLifeTimeInSeconds = tankInfo.BattleLifetime,
|
|
MarkOfMastery = tankInfo.Mastery
|
|
});
|
|
allOldData[historyCounter].TanksHistory[tankInfo.history.TankId] = tankInfo.history;
|
|
}
|
|
|
|
var p = new ImportProgress
|
|
{
|
|
Progress = 100 * (historyCounter + 1) / (2 * historyCount),
|
|
AccountName = accountInfo.Nickname,
|
|
LastBattle = accountInfoHistory.AccountInfoHistory.LastBattleTime.ToDateTime(),
|
|
BattlesCount = accountInfoHistory.AccountInfoHistory.Battles ?? 0,
|
|
TanksCount = oldTanksListByUpdateDate.Count
|
|
};
|
|
progress.Report(p);
|
|
historyCounter++;
|
|
}
|
|
}
|
|
|
|
if (connection.State.Equals(ConnectionState.Open))
|
|
{
|
|
await connection.CloseAsync();
|
|
}
|
|
}
|
|
}
|
|
|
|
private async Task<int> GetAccountHistoryCount(long accountId, DbConnection connection)
|
|
{
|
|
using (var command = connection.CreateCommand())
|
|
{
|
|
command.CommandText = AccountHistoryCountQuery;
|
|
command.Parameters.Add(new SqlParameter("AccountId", SqlDbType.Int) { Value = accountId });
|
|
if (connection.State.Equals(ConnectionState.Closed)) { await connection.OpenAsync(); }
|
|
|
|
return (int)(await command.ExecuteScalarAsync() ?? 0);
|
|
}
|
|
}
|
|
|
|
private async Task<List<(AccountInfoHistory AccountInfoHistory, int UpdatedAt)>> GetAccountInfoHistory(long accountId, DbConnection connection)
|
|
{
|
|
var resultList = new List<(AccountInfoHistory AccountInfoHistory, int UpdatedAt)>();
|
|
// As far as AccountInfoHistory has properties that can be set only via constructor, we use SQLDataReader here instead of DBSet
|
|
using (var command = connection.CreateCommand())
|
|
{
|
|
command.CommandText = AccountInfoHistoryQuery;
|
|
command.Parameters.Add(new SqlParameter("AccountId", SqlDbType.Int) { Value = accountId });
|
|
if (connection.State.Equals(ConnectionState.Closed)) { await connection.OpenAsync(); }
|
|
|
|
using (var reader = await command.ExecuteReaderAsync())
|
|
{
|
|
while (await reader.ReadAsync())
|
|
{
|
|
var history = new AccountInfoHistory(reader.GetInt64(reader.GetOrdinal("AccountId")),
|
|
reader.GetInt32(reader.GetOrdinal("LastBattleTime")))
|
|
{
|
|
Battles = reader.GetInt64(reader.GetOrdinal("Battles")),
|
|
CapturePoints = reader.GetInt64(reader.GetOrdinal("CapturePoints")),
|
|
DamageDealt = reader.GetInt64(reader.GetOrdinal("DamageDealt")),
|
|
DamageReceived = reader.GetInt64(reader.GetOrdinal("DamageReceived")),
|
|
DroppedCapturePoints = reader.GetInt64(reader.GetOrdinal("DroppedCapturePoints")),
|
|
Frags = reader.GetInt64(reader.GetOrdinal("Frags")),
|
|
Frags8P = reader.GetInt64(reader.GetOrdinal("Frags8P")),
|
|
Hits = reader.GetInt64(reader.GetOrdinal("Hits")),
|
|
Losses = reader.GetInt64(reader.GetOrdinal("Losses")),
|
|
MaxFrags = reader.GetInt64(reader.GetOrdinal("MaxFrags")),
|
|
MaxFragsTankId = reader.GetInt64(reader.GetOrdinal("MaxFragsTankId")),
|
|
MaxXp = reader.GetInt64(reader.GetOrdinal("MaxXp")),
|
|
MaxXpTankId = reader.GetInt64(reader.GetOrdinal("MaxXpTankId")),
|
|
Shots = reader.GetInt64(reader.GetOrdinal("Shots")),
|
|
Spotted = reader.GetInt64(reader.GetOrdinal("Spotted")),
|
|
SurvivedBattles = reader.GetInt64(reader.GetOrdinal("SurvivedBattles")),
|
|
WinAndSurvived = reader.GetInt64(reader.GetOrdinal("WinAndSurvived")),
|
|
Wins = reader.GetInt64(reader.GetOrdinal("Wins")),
|
|
Xp = reader.GetInt64(reader.GetOrdinal("Xp")),
|
|
AvgTier = reader.GetDouble(reader.GetOrdinal("AvgTier")),
|
|
Wn7 = reader.GetDouble(reader.GetOrdinal("Wn7"))
|
|
};
|
|
resultList.Add((history, reader.GetInt32(reader.GetOrdinal("LastBattleTime"))));
|
|
}
|
|
}
|
|
}
|
|
|
|
return resultList;
|
|
}
|
|
|
|
private async Task<List<(MarkOfMastery Mastery, int BattleLifetime, TankInfoHistory history)>> GetTanksHistory(
|
|
long accountId,
|
|
int lastBattleTime,
|
|
DbConnection connection)
|
|
{
|
|
var resultList = new List<(MarkOfMastery Mastery, int BattleLifetime, TankInfoHistory history)>();
|
|
|
|
using (var command = connection.CreateCommand())
|
|
{
|
|
command.CommandText = TanksInfoHistoryQuery;
|
|
command.Parameters.Add(new SqlParameter("LastBattleTime", SqlDbType.DateTime) { Value = lastBattleTime.ToDateTime() });
|
|
command.Parameters.Add(new SqlParameter("AccountId", SqlDbType.Int) { Value = accountId });
|
|
if (connection.State.Equals(ConnectionState.Closed)) { await connection.OpenAsync(); }
|
|
|
|
using (var reader = await command.ExecuteReaderAsync())
|
|
{
|
|
while (await reader.ReadAsync())
|
|
{
|
|
var tankHistory = new TankInfoHistory(
|
|
reader.GetInt64(reader.GetOrdinal("AccountId")),
|
|
reader.GetInt64(reader.GetOrdinal("TankId")),
|
|
reader.GetInt32(reader.GetOrdinal("LastBattleTime")))
|
|
{
|
|
Battles = reader.GetInt64(reader.GetOrdinal("Battles")),
|
|
CapturePoints = reader.GetInt64(reader.GetOrdinal("CapturePoints")),
|
|
DamageDealt = reader.GetInt64(reader.GetOrdinal("DamageDealt")),
|
|
DamageReceived = reader.GetInt64(reader.GetOrdinal("DamageReceived")),
|
|
DroppedCapturePoints = reader.GetInt64(reader.GetOrdinal("DroppedCapturePoints")),
|
|
Frags = reader.GetInt64(reader.GetOrdinal("Frags")),
|
|
Frags8P = reader.GetInt64(reader.GetOrdinal("Frags8P")),
|
|
Hits = reader.GetInt64(reader.GetOrdinal("Hits")),
|
|
Losses = reader.GetInt64(reader.GetOrdinal("Losses")),
|
|
MaxFrags = reader.GetInt64(reader.GetOrdinal("MaxFrags")),
|
|
MaxXp = reader.GetInt64(reader.GetOrdinal("MaxXp")),
|
|
Shots = reader.GetInt64(reader.GetOrdinal("Shots")),
|
|
Spotted = reader.GetInt64(reader.GetOrdinal("Spotted")),
|
|
SurvivedBattles = reader.GetInt64(reader.GetOrdinal("SurvivedBattles")),
|
|
WinAndSurvived = reader.GetInt64(reader.GetOrdinal("WinAndSurvived")),
|
|
Wins = reader.GetInt64(reader.GetOrdinal("Wins")),
|
|
Xp = reader.GetInt64(reader.GetOrdinal("Xp")),
|
|
Wn7 = reader.GetDouble(reader.GetOrdinal("Wn7"))
|
|
};
|
|
resultList.Add((
|
|
(MarkOfMastery) reader.GetInt32(reader.GetOrdinal("MarkOfMastery")),
|
|
reader.GetInt32(reader.GetOrdinal("BattleLifeTimeInSeconds")),
|
|
tankHistory));
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
return resultList;
|
|
}
|
|
|
|
private async Task<OldStatisticsDbContext> CreateDbContext(string connection)
|
|
{
|
|
var contextBuilder = new DbContextOptionsBuilder();
|
|
contextBuilder.UseSqlServer(connection);
|
|
|
|
var context = new OldStatisticsDbContext(contextBuilder.Options);
|
|
var connectionValid = await context.Database.CanConnectAsync();
|
|
|
|
if (!connectionValid)
|
|
{
|
|
await context.DisposeAsync();
|
|
throw new ApplicationException("Connection string is not valid");
|
|
}
|
|
|
|
return context;
|
|
}
|
|
|
|
private const string FetchAllAccountsQuery = @"
|
|
select AccountId AccountId,
|
|
wotb.to_unix_time(AccountCreatedAt) CreatedAt,
|
|
wotb.to_unix_time(LastBattleTime) LastBattleTime,
|
|
NickName Nickname
|
|
from wotb.AccountInfo
|
|
where AccountCreatedAt is not null and LastBattleTime is not null";
|
|
|
|
private const string AccountHistoryCountQuery = @"
|
|
select COUNT(*)
|
|
from wotb.AccountInfoStatistics
|
|
where AccountId = @AccountId";
|
|
|
|
private const string SelectAccountInfoQuery = @"
|
|
select AccountId AccountId,
|
|
wotb.to_unix_time(AccountCreatedAt) CreatedAt,
|
|
wotb.to_unix_time(LastBattleTime) LastBattleTime,
|
|
NickName Nickname
|
|
from wotb.AccountInfo
|
|
where AccountId = @AccountId";
|
|
|
|
private const string AccountInfoHistoryQuery = @"
|
|
select
|
|
s.AccountId,
|
|
wotb.to_unix_time(s.UpdatedAt) LastBattleTime,
|
|
s.Battles,
|
|
s.CapturePoints,
|
|
s.DamageDealt,
|
|
s.DamageReceived,
|
|
s.DroppedCapturePoints,
|
|
s.Frags,
|
|
s.Frags8P,
|
|
s.Hits,
|
|
s.Losses,
|
|
s.MaxFrags,
|
|
s.MaxFragsTankId,
|
|
s.MaxXp,
|
|
s.MaxXpTankId,
|
|
s.Shots,
|
|
s.Spotted,
|
|
s.SurvivedBattles,
|
|
s.WinAndSurvived,
|
|
s.Wins,
|
|
s.Xp,
|
|
s.AvgTier,
|
|
s.Wn7
|
|
from wotb.AccountInfoStatistics s
|
|
where s.AccountId = @AccountId
|
|
order by s.UpdatedAt";
|
|
|
|
private const string TanksInfoHistoryQuery = @"
|
|
select
|
|
t.AccountId,
|
|
t.TankId,
|
|
wotb.to_unix_time(t.LastBattleTime) LastBattleTime,
|
|
t.BattleLifeTimeInSeconds,
|
|
t.MarkOfMastery,
|
|
t.Battles,
|
|
t.CapturePoints,
|
|
t.DamageDealt,
|
|
t.DamageReceived,
|
|
t.DroppedCapturePoints,
|
|
t.Frags,
|
|
t.Frags8P,
|
|
t.Hits,
|
|
t.Losses,
|
|
t.MaxFrags,
|
|
t.MaxXp,
|
|
t.Shots,
|
|
t.Spotted,
|
|
t.SurvivedBattles,
|
|
t.WinAndSurvived,
|
|
t.Wins,
|
|
t.Xp,
|
|
t.Wn7
|
|
from wotb.AccountTankStatistics t
|
|
inner join (select MAX(LastBattleTime) MaxBattleTime, AccountId, TankId from wotb.AccountTankStatistics where LastBattleTime <= @LastBattleTime group by AccountId, TankId) q
|
|
on q.MaxBattleTime = t.LastBattleTime and q.AccountId = t.AccountId and q.TankId = t.TankId
|
|
where t.AccountId = @AccountId
|
|
order by t.LastBattleTime desc";
|
|
|
|
#endregion
|
|
}
|
|
} |