AccountInfo history statistics query

This commit is contained in:
Mikhail Shabanov committed 2020-12-23 16:34:12 +01:00
1 parent ec8f434f42
commit 758aa10e55
18 files changed
+522 -28

No files matched your search

@@ -0,0 +1,26 @@
using System;
using WotBlitzStatisticsPro.Common.Model;
using WotBlitzStatisticsPro.DataAccess.Model.Accounts;
namespace WotBlitzStatisticsPro.Logic.AccountInformationPipeline
{
public class AccountHistoryInformationPipelineContextData: IDatabasePipelineContextData, IStatisticsPipelineData
{
public AccountHistoryInformationPipelineContextData(DateTime lastBattleSince)
{
LastBattleSince = lastBattleSince;
}
public DateTime LastBattleSince { get; }
public AccountInfo DbAccountInfo { get; set; }
public IStatistics[] History { get; set; }
public ShortStatistics OverallStatistics { get; set; }
public ShortStatistics PeriodAccountStatistics { get; set; }
public StatisticsDifference PeriodDifference { get; set; }
public StatisticsDifference[] StatisticsHistory { get; set; }
public AccountInfoHistoryResponse Response { get; set; }
}
}
@@ -1,13 +0,0 @@
using WotBlitzStatisticsPro.Common.Model;
using WotBlitzStatisticsPro.DataAccess.Model.Accounts;
namespace WotBlitzStatisticsPro.Logic.AccountInformationPipeline
{
public class HistoryInformationPipelineContextData: IDatabasePipelineContextData
{
public AccountInfo DbAccountInfo { get; set; }
public AccountInfoHistoryResponse Response { get; set; }
}
}
@@ -0,0 +1,18 @@
using WotBlitzStatisticsPro.Common.Model;
namespace WotBlitzStatisticsPro.Logic.AccountInformationPipeline
{
public interface IStatisticsPipelineData
{
IStatistics[] History { get; set; }
ShortStatistics OverallStatistics { get; set; }
public ShortStatistics PeriodAccountStatistics { get; set; }
public StatisticsDifference PeriodDifference { get; set; }
public StatisticsDifference[] StatisticsHistory { get; set; }
}
}
@@ -0,0 +1,34 @@
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using WotBlitzStatisticsPro.Logic.AccountInformationPipeline.OperationContext;
using WotBlitzStatisticsPro.Logic.Pipeline;
namespace WotBlitzStatisticsPro.Logic.AccountInformationPipeline.Operations
{
public class CheckIfHistoryIsEmptyOperation : IOperation<IOperationContext>
{
private readonly ILogger<CheckIfHistoryIsEmptyOperation> _logger;
public CheckIfHistoryIsEmptyOperation(ILogger<CheckIfHistoryIsEmptyOperation> logger)
{
_logger = logger;
}
public Task Invoke(IOperationContext context, Func<IOperationContext, Task> next)
{
var contextData = context.Get<IStatisticsPipelineData>();
if (contextData.History == null ||
contextData.History.Length == 0)
{
_logger.LogInformation(
$"History request doesn't return any data");
return Task.CompletedTask;
}
return next.Invoke(context);
}
}
}
@@ -0,0 +1,30 @@
using System;
using System.Threading.Tasks;
using WotBlitzStatisticsPro.Common.Model;
using WotBlitzStatisticsPro.Logic.AccountInformationPipeline.OperationContext;
using WotBlitzStatisticsPro.Logic.Calculations;
using WotBlitzStatisticsPro.Logic.Pipeline;
namespace WotBlitzStatisticsPro.Logic.AccountInformationPipeline.Operations
{
public class FillAccountInfoHistoryResponse : IOperation<IOperationContext>
{
public Task Invoke(IOperationContext context, Func<IOperationContext, Task> next)
{
var contextData = context.Get<AccountHistoryInformationPipelineContextData>();
contextData.Response = new AccountInfoHistoryResponse
{
AccountId = context.Request.AccountId,
CreatedAt = contextData.DbAccountInfo.CreatedAt.ToDateTime(),
Nickname = contextData.DbAccountInfo.Nickname,
PeriodDifference = contextData.PeriodDifference,
StatisticsHistory = contextData.StatisticsHistory,
PeriodAccountStatistics = contextData.PeriodAccountStatistics,
OverallAccountStatistics = contextData.OverallStatistics
};
return next.Invoke(context);
}
}
}
@@ -0,0 +1,29 @@
using System;
using System.Threading.Tasks;
using AutoMapper;
using WotBlitzStatisticsPro.Common.Model;
using WotBlitzStatisticsPro.Logic.AccountInformationPipeline.OperationContext;
using WotBlitzStatisticsPro.Logic.Pipeline;
namespace WotBlitzStatisticsPro.Logic.AccountInformationPipeline.Operations
{
public class FillOverallStatisticsOperation : IOperation<IOperationContext>
{
private readonly IMapper _mapper;
public FillOverallStatisticsOperation(IMapper mapper)
{
_mapper = mapper;
}
public Task Invoke(IOperationContext context, Func<IOperationContext, Task> next)
{
var contextData = context.Get<IStatisticsPipelineData>();
contextData.OverallStatistics = _mapper.Map<IStatistics, ShortStatistics>(contextData.History[0]);
return next.Invoke(context);
}
}
}
@@ -0,0 +1,28 @@
using System;
using System.Threading.Tasks;
using AutoMapper;
using WotBlitzStatisticsPro.Common.Model;
using WotBlitzStatisticsPro.Logic.AccountInformationPipeline.OperationContext;
using WotBlitzStatisticsPro.Logic.Calculations;
using WotBlitzStatisticsPro.Logic.Pipeline;
namespace WotBlitzStatisticsPro.Logic.AccountInformationPipeline.Operations
{
public class FillPeriodDifferenceOperation : IOperation<IOperationContext>
{
public Task Invoke(IOperationContext context, Func<IOperationContext, Task> next)
{
var contextData = context.Get<IStatisticsPipelineData>();
if (contextData.History.Length > 1)
{
contextData.PeriodDifference = new StatisticsDifference();
var historyLastIndex = contextData.History.Length - 1;
contextData.PeriodDifference.FillDifference(contextData.History[0], contextData.History[historyLastIndex]);
}
return next.Invoke(context);
}
}
}
@@ -0,0 +1,42 @@
using System;
using System.Threading.Tasks;
using AutoMapper;
using WotBlitzStatisticsPro.Common.Model;
using WotBlitzStatisticsPro.DataAccess.Model.Accounts;
using WotBlitzStatisticsPro.Logic.AccountInformationPipeline.OperationContext;
using WotBlitzStatisticsPro.Logic.Pipeline;
namespace WotBlitzStatisticsPro.Logic.AccountInformationPipeline.Operations
{
public class FillPeriodStatisticsOperation : IOperation<IOperationContext>
{
private readonly IMapper _mapper;
public FillPeriodStatisticsOperation(IMapper mapper)
{
_mapper = mapper;
}
public Task Invoke(IOperationContext context, Func<IOperationContext, Task> next)
{
var contextData = context.Get<IStatisticsPipelineData>();
if (contextData.History.Length > 1)
{
var diff = new AccountInfoHistory(context.Request.AccountId, contextData.History[0].LastBattleTime);
var historyLastIndex = contextData.History.Length - 1;
diff.Battles = contextData.History[0].Battles - contextData.History[historyLastIndex].Battles;
diff.Wins = contextData.History[0].Wins - contextData.History[historyLastIndex].Wins;
diff.DamageDealt = contextData.History[0].DamageDealt - contextData.History[historyLastIndex].DamageDealt;
diff.Xp = contextData.History[0].Xp - contextData.History[historyLastIndex].Xp;
contextData.PeriodAccountStatistics = _mapper.Map<IStatistics, ShortStatistics>(diff);
}
return next.Invoke(context);
}
}
}
@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using WotBlitzStatisticsPro.Common.Model;
using WotBlitzStatisticsPro.Logic.AccountInformationPipeline.OperationContext;
using WotBlitzStatisticsPro.Logic.Calculations;
using WotBlitzStatisticsPro.Logic.Pipeline;
namespace WotBlitzStatisticsPro.Logic.AccountInformationPipeline.Operations
{
public class FillStatisticsDifferenceOperation : IOperation<IOperationContext>
{
public Task Invoke(IOperationContext context, Func<IOperationContext, Task> next)
{
var contextData = context.Get<IStatisticsPipelineData>();
if (contextData.History.Length > 1)
{
contextData.StatisticsHistory = new StatisticsDifference[contextData.History.Length - 1];
for (int i = 1; i < contextData.History.Length; i++)
{
contextData.StatisticsHistory[i - 1] = new StatisticsDifference();
contextData.StatisticsHistory[i - 1].FillDifference(contextData.History[i - 1], contextData.History[i]);
}
}
return next.Invoke(context);
}
}
}
@@ -0,0 +1,28 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using WotBlitzStatisticsPro.DataAccess;
using WotBlitzStatisticsPro.Logic.AccountInformationPipeline.OperationContext;
using WotBlitzStatisticsPro.Logic.Calculations;
using WotBlitzStatisticsPro.Logic.Pipeline;
namespace WotBlitzStatisticsPro.Logic.AccountInformationPipeline.Operations
{
public class ReadAccountInfoHistoryFromDbOperation : IOperation<IOperationContext>
{
private readonly IWargamingAccountDataAccessor _accountDataAccessor;
public ReadAccountInfoHistoryFromDbOperation(IWargamingAccountDataAccessor accountDataAccessor)
{
_accountDataAccessor = accountDataAccessor;
}
public async Task Invoke(IOperationContext context, Func<IOperationContext, Task> next)
{
var contextData = context.Get<AccountHistoryInformationPipelineContextData>();
contextData.History = (await _accountDataAccessor.GetAccountHistory(context.Request.AccountId, contextData.LastBattleSince.ToUnixTimestamp())).ToArray();
await next.Invoke(context);
}
}
}
@@ -12,6 +12,12 @@ namespace WotBlitzStatisticsPro.Logic.Calculations
return dtDateTime;
}
public static int ToUnixTimestamp(this DateTime date)
{
var diff = date - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
return Convert.ToInt32(diff.TotalSeconds);
}
}
}
@@ -0,0 +1,77 @@
using WotBlitzStatisticsPro.Common.Model;
namespace WotBlitzStatisticsPro.Logic.Calculations
{
public static class StatisticsHelper
{
public static void FillDifference(
this StatisticsDifference difference,
IStatistics firstItem,
IStatistics lastItem)
{
difference.LastBattleTime = firstItem.LastBattleTime.ToDateTime();
difference.Battles = new StatisticsDifferenceItem<long>
{
CurrentValue = firstItem.Battles ?? 0,
Difference = firstItem.Battles - lastItem.Battles
};
difference.AvgTier = new StatisticsDifferenceItem<double>
{
CurrentValue = firstItem.AvgTier,
Difference = firstItem.AvgTier - lastItem.AvgTier
};
difference.Wn7 = new StatisticsDifferenceItem<double>
{
CurrentValue = firstItem.Wn7,
Difference = firstItem.Wn7 - lastItem.Wn7
};
decimal firstWinRate = lastItem.Battles.HasValue
? 100 * (decimal)(lastItem.Wins ?? 0) /
lastItem.Battles.Value
: 0m;
decimal lastWinRate = firstItem.Battles.HasValue
? 100 * (decimal)(firstItem.Wins ?? 0) /
firstItem.Battles.Value
: 0m;
difference.WinRate = new StatisticsDifferenceItem<decimal>
{
CurrentValue = lastWinRate,
Difference = lastWinRate - firstWinRate
};
decimal firsAvgDmg = lastItem.Battles.HasValue
? (decimal)(lastItem.DamageDealt ?? 0) /
lastItem.Battles.Value
: 0m;
decimal lastAvgDmg = firstItem.Battles.HasValue
? (decimal)(firstItem.DamageDealt ?? 0) /
firstItem.Battles.Value
: 0m;
difference.AvgDamage = new StatisticsDifferenceItem<decimal>
{
CurrentValue = lastAvgDmg,
Difference = lastAvgDmg - firsAvgDmg
};
decimal firsAvgXp = lastItem.Battles.HasValue
? (decimal)(lastItem.Xp ?? 0) /
lastItem.Battles.Value
: 0m;
decimal lastAvgXp = firstItem.Battles.HasValue
? (decimal)(firstItem.Xp ?? 0) /
firstItem.Battles.Value
: 0m;
difference.AvgXp = new StatisticsDifferenceItem<decimal>
{
CurrentValue = lastAvgXp,
Difference = lastAvgXp - firsAvgXp
};
}
}
}
@@ -0,0 +1,23 @@
using AutoMapper;
using WotBlitzStatisticsPro.Common.Model;
using WotBlitzStatisticsPro.Logic.Calculations;
namespace WotBlitzStatisticsPro.Logic.Mappers
{
public class ShortStatisticsProfile: Profile
{
public ShortStatisticsProfile()
{
CreateMap<IStatistics, ShortStatistics>()
.ForMember(d => d.LastBattleTime,
o => o.MapFrom(s => s.LastBattleTime.ToDateTime()))
.ForMember(d => d.AvgDamage,
o => o.MapFrom(s => s.Battles.HasValue && s.Battles > 0 ? ((decimal)s.DamageDealt / s.Battles) : 0m))
.ForMember(d => d.AvgXp,
o => o.MapFrom(s => s.Battles.HasValue && s.Battles > 0 ? ((decimal)s.Xp / s.Battles) : 0m))
.ForMember(d => d.WinRate,
o => o.MapFrom(s => s.Battles.HasValue && s.Battles > 0 ? (100 * (decimal)s.Wins / s.Battles) : 0m))
;
}
}
}
@@ -62,20 +62,19 @@ namespace WotBlitzStatisticsPro.Logic
public async Task<AccountInfoHistoryResponse> GetAccountInfoHistory(RealmType realm, long accountId, DateTime startDate, RequestLanguage requestLanguage)
{
var contextData = new HistoryInformationPipelineContextData();
var contextData = new AccountHistoryInformationPipelineContextData(startDate);
var context = new OperationContext(new AccountRequest(accountId, realm, requestLanguage));
context.AddOrReplace(contextData);
var pipeline = new Pipeline<IOperationContext>(_operationFactory);
// ToDo:
// 1. First read account info from DB operation
pipeline.AddOperation<ReadAccountInfoFromDbOperation>()
// 2. Read account history from DB at the startDate !Array SortedByDate! operation
// 3. Fill the OverallAccountStatistics
// PeriodAccountStatistics
// PeriodDifference
// StatisticsHistory
.AddOperation<ReadAccountInfoHistoryFromDbOperation>()
.AddOperation<CheckIfHistoryIsEmptyOperation>()
.AddOperation<FillOverallStatisticsOperation>()
.AddOperation<FillPeriodStatisticsOperation>()
.AddOperation<FillPeriodDifferenceOperation>()
.AddOperation<FillStatisticsDifferenceOperation>()
.AddOperation<FillAccountInfoHistoryResponse>()
;
await pipeline.Build()
@@ -66,6 +66,13 @@ namespace WotBlitzStatisticsPro.Logic
services.AddTransient<CalculateStatisticsOperation>();
services.AddTransient<BuildAccountInfoResponseOperation>();
services.AddTransient<SaveAccountAndTanksOperation>();
services.AddTransient<ReadAccountInfoHistoryFromDbOperation>();
services.AddTransient<CheckIfHistoryIsEmptyOperation>();
services.AddTransient<FillOverallStatisticsOperation>();
services.AddTransient<FillPeriodStatisticsOperation>();
services.AddTransient<FillPeriodDifferenceOperation>();
services.AddTransient<FillStatisticsDifferenceOperation>();
services.AddTransient<FillAccountInfoHistoryResponse>();
services.AddTransient<IOperationFactory>(serviceProvider =>
new ServiceProviderOperationFactory(serviceProvider));