diff --git a/WotBlitzStatisticsPro.DataAccess/DictionariesDataAccessor.cs b/WotBlitzStatisticsPro.DataAccess/DictionariesDataAccessor.cs index f5417c7..a391a5b 100644 --- a/WotBlitzStatisticsPro.DataAccess/DictionariesDataAccessor.cs +++ b/WotBlitzStatisticsPro.DataAccess/DictionariesDataAccessor.cs @@ -91,5 +91,15 @@ namespace WotBlitzStatisticsPro.DataAccess .FindAsync(Builders.Filter.Empty); return collection.ToList(); } + + public async Task> GetTankTires(long[] tankIds) + { + var collection = await _database.GetCollection(VehiclesCollectionName) + .Find(Builders.Filter.In(v => v.TankId, tankIds)) + .Project(v => new { TankId = v.TankId, Tier = v.Tier }) + .ToListAsync(); + + return collection.ToDictionary(k => k.TankId, v => v.Tier); + } } } \ No newline at end of file diff --git a/WotBlitzStatisticsPro.DataAccess/IDictionariesDataAccessor.cs b/WotBlitzStatisticsPro.DataAccess/IDictionariesDataAccessor.cs index 534d0ac..637373d 100644 --- a/WotBlitzStatisticsPro.DataAccess/IDictionariesDataAccessor.cs +++ b/WotBlitzStatisticsPro.DataAccess/IDictionariesDataAccessor.cs @@ -21,5 +21,7 @@ namespace WotBlitzStatisticsPro.DataAccess Task UpdateVehicles(List vehiclesDictionary); Task> ReadLanguages(); + + Task> GetTankTires(long[] tankIds); } } \ No newline at end of file diff --git a/WotBlitzStatisticsPro.DataAccess/Model/Accounts/TankInfo.cs b/WotBlitzStatisticsPro.DataAccess/Model/Accounts/TankInfo.cs index 8191d8e..8331965 100644 --- a/WotBlitzStatisticsPro.DataAccess/Model/Accounts/TankInfo.cs +++ b/WotBlitzStatisticsPro.DataAccess/Model/Accounts/TankInfo.cs @@ -1,7 +1,40 @@ -namespace WotBlitzStatisticsPro.DataAccess.Model.Accounts +using WotBlitzStatisticsPro.Common.Model; + +namespace WotBlitzStatisticsPro.DataAccess.Model.Accounts { public class TankInfo { - + /// + /// Player account identifier + /// + public long AccountId { get; set; } + + /// + /// Tank identifier + /// + public long TankId { get; set; } + + + public int BattleLifeTimeInSeconds { get; set; } + + /// + /// Last battle + /// + public int LastBattleTime { get; set; } + + public bool? InGarage { get; set; } + + private int? InGarageUpdated { get; set; } + + /// + /// Mastery: + /// + /// 0 — None + /// 1 — Rank 3 + /// 2 — Rank 2 + /// 3 — Rank 1 + /// 4 — Mastery + /// + public MarkOfMastery MarkOfMastery { get; set; } } } \ No newline at end of file diff --git a/WotBlitzStatisticsPro.DataAccess/Model/Accounts/TankInfoHistory.cs b/WotBlitzStatisticsPro.DataAccess/Model/Accounts/TankInfoHistory.cs new file mode 100644 index 0000000..91f4791 --- /dev/null +++ b/WotBlitzStatisticsPro.DataAccess/Model/Accounts/TankInfoHistory.cs @@ -0,0 +1,113 @@ +namespace WotBlitzStatisticsPro.DataAccess.Model.Accounts +{ + public class TankInfoHistory + { + /// + /// Player account identifier + /// + public long AccountId { get; set; } + + /// + /// Tank identifier + /// + public long TankId { get; set; } + + /// + /// Last battle + /// + public int LastBattleTime { get; set; } + + + + + /// + /// Battles count + /// + public long? Battles { get; set; } + + /// + /// Base capture points + /// + public long? CapturePoints { get; set; } + + /// + /// Damage dealt + /// + public long? DamageDealt { get; set; } + + /// + /// Damage received + /// + public long? DamageReceived { get; set; } + + /// + /// Base dropped capture points + /// + public long? DroppedCapturePoints { get; set; } + + /// + /// Frags count + /// + public long? Frags { get; set; } + + /// + /// Frags count after 8 tier + /// + public long? Frags8P { get; set; } + + /// + /// Hits count + /// + public long? Hits { get; set; } + + /// + /// Losses count + /// + public long? Losses { get; set; } + + /// + /// Max frags per battle + /// + public long? MaxFrags { get; set; } + + /// + /// Max XP per battle + /// + public long? MaxXp { get; set; } + + /// + /// Shots count + /// + public long? Shots { get; set; } + + /// + /// Spotted vehicles count + /// + public long? Spotted { get; set; } + + /// + /// Amount of survived battles + /// + public long? SurvivedBattles { get; set; } + + /// + /// Amount of survived and wined battles + /// + public long? WinAndSurvived { get; set; } + + /// + /// Wins count + /// + public long? Wins { get; set; } + + /// + /// Total experience + /// + public long? Xp { get; set; } + + /// + /// Wn7 coefficient + /// + public double Wn7 { get; set; } + } +} \ No newline at end of file diff --git a/WotBlitzStatisticsPro.Logic/AccountInformationPipeline/AccountInformationPipelineContext.cs b/WotBlitzStatisticsPro.Logic/AccountInformationPipeline/AccountInformationPipelineContext.cs index ab29c99..afa27e5 100644 --- a/WotBlitzStatisticsPro.Logic/AccountInformationPipeline/AccountInformationPipelineContext.cs +++ b/WotBlitzStatisticsPro.Logic/AccountInformationPipeline/AccountInformationPipelineContext.cs @@ -1,4 +1,5 @@ -using WotBlitzStatisticsPro.Common.Model; +using System.Collections.Generic; +using WotBlitzStatisticsPro.Common.Model; using WotBlitzStatisticsPro.DataAccess.Model.Accounts; namespace WotBlitzStatisticsPro.Logic.AccountInformationPipeline @@ -24,5 +25,9 @@ namespace WotBlitzStatisticsPro.Logic.AccountInformationPipeline public AccountInfoHistory AccountInfoHistory { get; set; } + public List Tanks { get; set; } + + public Dictionary TanksHistory { get; set; } + } } \ No newline at end of file diff --git a/WotBlitzStatisticsPro.Logic/AccountInformationPipeline/Operations/CalculateStatisticsOperation.cs b/WotBlitzStatisticsPro.Logic/AccountInformationPipeline/Operations/CalculateStatisticsOperation.cs new file mode 100644 index 0000000..0e30612 --- /dev/null +++ b/WotBlitzStatisticsPro.Logic/AccountInformationPipeline/Operations/CalculateStatisticsOperation.cs @@ -0,0 +1,39 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using WotBlitzStatisticsPro.DataAccess; +using WotBlitzStatisticsPro.Logic.Calculations; +using WotBlitzStatisticsPro.Logic.Pipeline; + +namespace WotBlitzStatisticsPro.Logic.AccountInformationPipeline.Operations +{ + public class CalculateStatisticsOperation : IOperation + { + private readonly IDictionariesDataAccessor _dictionariesDataAccessor; + + public CalculateStatisticsOperation(IDictionariesDataAccessor dictionariesDataAccessor) + { + _dictionariesDataAccessor = dictionariesDataAccessor; + } + + public async Task Invoke(AccountInformationPipelineContext context, Func next) + { + var tankIds = context.Tanks.Select(t => t.TankId).ToArray(); + var tankTires = await _dictionariesDataAccessor.GetTankTires(tankIds); + + foreach (var tankInfoHistory in context.TanksHistory) + { + if (!tankTires.ContainsKey(tankInfoHistory.Key)) + { + continue; + } + tankInfoHistory.Value.CalculateWn7(tankTires[tankInfoHistory.Key]); + } + + context.AccountInfoHistory.CalculateMiddleTier(context.TanksHistory.Values.ToList(), tankTires); + context.AccountInfoHistory.CalculateWn7(); + + await next.Invoke(context); + } + } +} \ No newline at end of file diff --git a/WotBlitzStatisticsPro.Logic/AccountInformationPipeline/Operations/GetTanksInfoOperation.cs b/WotBlitzStatisticsPro.Logic/AccountInformationPipeline/Operations/GetTanksInfoOperation.cs index 53b75c8..187a6d3 100644 --- a/WotBlitzStatisticsPro.Logic/AccountInformationPipeline/Operations/GetTanksInfoOperation.cs +++ b/WotBlitzStatisticsPro.Logic/AccountInformationPipeline/Operations/GetTanksInfoOperation.cs @@ -1,8 +1,11 @@ using System; +using System.Collections.Generic; using System.Threading.Tasks; using AutoMapper; +using WotBlitzStatisticsPro.DataAccess.Model.Accounts; using WotBlitzStatisticsPro.Logic.Pipeline; using WotBlitzStatisticsPro.WgApiClient; +using WotBlitzStatisticsPro.WgApiClient.Model; namespace WotBlitzStatisticsPro.Logic.AccountInformationPipeline.Operations { @@ -24,6 +27,19 @@ namespace WotBlitzStatisticsPro.Logic.AccountInformationPipeline.Operations var tanksInfo = await _wargamingApi.GetPlayerAccountTanksInfo(context.AccountId, context.RealmType, context.RequestLanguage); + context.Tanks = new List(); + context.TanksHistory = new Dictionary(); + + tanksInfo.ForEach(tank => + { + context.Tanks.Add(_mapper.Map(tank)); + var stat = _mapper.Map(tank.All); + stat.AccountId = tank.AccountId; + stat.TankId = tank.TankId; + stat.LastBattleTime = tank.LastBattleTime; + context.TanksHistory[stat.TankId] = stat; + }); + await next.Invoke(context); } } diff --git a/WotBlitzStatisticsPro.Logic/Calculations/TierHelper.cs b/WotBlitzStatisticsPro.Logic/Calculations/TierHelper.cs new file mode 100644 index 0000000..d9763d1 --- /dev/null +++ b/WotBlitzStatisticsPro.Logic/Calculations/TierHelper.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; +using WotBlitzStatisticsPro.DataAccess.Model.Accounts; + +namespace WotBlitzStatisticsPro.Logic.Calculations +{ + public static class TierHelper + { + public static void CalculateMiddleTier(this AccountInfoHistory account, IList allTanks, Dictionary tankTires) + { + double x = 0d; + if (!account.Battles.HasValue) + { + return; + } + foreach (var tank in allTanks) + { + if (tankTires.ContainsKey(tank.TankId) && tank.Battles.HasValue && tank.Battles > 0) + { + x += tankTires[tank.TankId] * tank.Battles.Value; + } + } + if (account.Battles > 0) + x /= account.Battles.Value; + account.AvgTier = x; + } + + } +} \ No newline at end of file diff --git a/WotBlitzStatisticsPro.Logic/Calculations/Wn7Helper.cs b/WotBlitzStatisticsPro.Logic/Calculations/Wn7Helper.cs new file mode 100644 index 0000000..7b0e769 --- /dev/null +++ b/WotBlitzStatisticsPro.Logic/Calculations/Wn7Helper.cs @@ -0,0 +1,114 @@ +using System; +using WotBlitzStatisticsPro.DataAccess.Model.Accounts; + +namespace WotBlitzStatisticsPro.Logic.Calculations +{ + public static class Wn7Helper + { + public static double CalculateWn7( + long battles, + double tier, + double avdFrags, + double avgDamage, + double avgSpot, + double avgDef, + double winRate) + { + double firstSummation = CalculateFirstSummation(tier, avdFrags); + double secondSummation = CalculateSecondSummation(tier, avgDamage); + double thirdSummation = CalculateThirdSummation(tier, avgSpot); + double fourthSummation = CalculateFourthSummation(avgDef); + double fifthSummation = CalculateFifthSummation(winRate); + double sixthSummation = CalculateSixthSummation(tier, battles); + + return firstSummation + secondSummation + thirdSummation + fourthSummation + fifthSummation + sixthSummation; + } + + public static void CalculateWn7(this TankInfoHistory tank, double tier) + { + if (!tank.Battles.HasValue) + { + return; + } + double avdFrags = tank.Frags.DoubleValue() / tank.Battles.DoubleValue(); + double avgDamage = tank.DamageDealt.DoubleValue() / tank.Battles.DoubleValue(); + double avgSpot = tank.Spotted.DoubleValue() / tank.Battles.DoubleValue(); + double avgDef = tank.DroppedCapturePoints.DoubleValue() / tank.Battles.DoubleValue(); + double winRate = (100d * tank.Wins.DoubleValue() / tank.Battles.DoubleValue()); // - 48; + + tank.Wn7 = CalculateWn7(tank.Battles.Value, tier, avdFrags, avgDamage, avgSpot, avgDef, winRate); + } + + public static void CalculateWn7(this AccountInfoHistory account) + { + if (!account.Battles.HasValue) + { + return; + } + + double avdFrags = account.Frags.DoubleValue() / account.Battles.DoubleValue(); + double avgDamage = account.DamageDealt.DoubleValue() / account.Battles.DoubleValue(); + double avgSpot = account.Spotted.DoubleValue() / account.Battles.DoubleValue(); + double avgDef = account.DroppedCapturePoints.DoubleValue() / account.Battles.DoubleValue(); + double winRate = (100 * account.Wins.DoubleValue() / account.Battles.DoubleValue()); // - 48; + + account.Wn7 = CalculateWn7(account.Battles.Value, account.AvgTier, avdFrags, avgDamage, avgSpot, avgDef, winRate); + } + + + //(1240-1040/(min(TIER,6)^0.164))*FRAGS + private static double CalculateFirstSummation(double tier, double frags) + { + double x = Math.Min(tier, 6); + x = Math.Pow(x, 0.164d); + x = 1040d / x; + x = 1240d - x; + return x * frags; + } + + //DAMAGE * 530/(184*exp(0.24*TIER)+130) + private static double CalculateSecondSummation(double tier, double damage) + { + double x = 184d * Math.Exp(0.24d * tier) + 130d; + return damage * 530d / x; + } + + //SPOT * 125*min(TIER,3)/3 + private static double CalculateThirdSummation(double tier, double spot) + { + return spot * 125d * Math.Min(tier, 3d) / 3; + } + + //min(DEF,2.2)*100 + private static double CalculateFourthSummation(double def) + { + return Math.Min(def, 2.2d) * 100; + } + + //((185/(0.17+exp(((WINRATE)-35)*-0.134)))-500)*0.45 + private static double CalculateFifthSummation(double winRate) + { + double x = (winRate - 35d) * -0.134; + x = Math.Exp(x) + 0.17d; + return (185d / x - 500d) * 0.45d; + } + + //(-1*(((5 - min(TIER,5))*125)/(1 + exp((TIER-(TOTAL/220^(3/TIER)))*1.5)))) + private static double CalculateSixthSummation(double tier, double total) + { + double x = -125d * (5d - Math.Min(tier, 5d)); + double y = total / Math.Pow(220d, 3d / tier); + y = 1.5d * (tier - y); + y = Math.Exp(y) + 1d; + return x / y; + } + + public static double DoubleValue(this long? val) + { + if (!val.HasValue) + return 0d; + return (double)val.Value; + } + + } +} \ No newline at end of file diff --git a/WotBlitzStatisticsPro.Logic/Mappers/TankInfoProfile.cs b/WotBlitzStatisticsPro.Logic/Mappers/TankInfoProfile.cs new file mode 100644 index 0000000..64b658b --- /dev/null +++ b/WotBlitzStatisticsPro.Logic/Mappers/TankInfoProfile.cs @@ -0,0 +1,14 @@ +using AutoMapper; +using WotBlitzStatisticsPro.DataAccess.Model.Accounts; +using WotBlitzStatisticsPro.WgApiClient.Model; + +namespace WotBlitzStatisticsPro.Logic.Mappers +{ + public class TankInfoProfile : Profile + { + public TankInfoProfile() + { + CreateMap(); + } + } +} \ No newline at end of file diff --git a/WotBlitzStatisticsPro.Logic/Mappers/TankInfoStatisticsProfile.cs b/WotBlitzStatisticsPro.Logic/Mappers/TankInfoStatisticsProfile.cs new file mode 100644 index 0000000..340bd5e --- /dev/null +++ b/WotBlitzStatisticsPro.Logic/Mappers/TankInfoStatisticsProfile.cs @@ -0,0 +1,14 @@ +using AutoMapper; +using WotBlitzStatisticsPro.DataAccess.Model.Accounts; +using WotBlitzStatisticsPro.WgApiClient.Model; + +namespace WotBlitzStatisticsPro.Logic.Mappers +{ + public class TankInfoStatisticsProfile: Profile + { + public TankInfoStatisticsProfile() + { + CreateMap(); + } + } +} \ No newline at end of file diff --git a/WotBlitzStatisticsPro.Logic/WargamingAccounts.cs b/WotBlitzStatisticsPro.Logic/WargamingAccounts.cs index 47c8081..0f9baa8 100644 --- a/WotBlitzStatisticsPro.Logic/WargamingAccounts.cs +++ b/WotBlitzStatisticsPro.Logic/WargamingAccounts.cs @@ -22,6 +22,7 @@ namespace WotBlitzStatisticsPro.Logic pipeline.AddOperation() .AddOperation() + .AddOperation() ; // ToDo: add other operations diff --git a/WotBlitzStatisticsPro.Logic/WotBlitzStatisticsLogicInstaller.cs b/WotBlitzStatisticsPro.Logic/WotBlitzStatisticsLogicInstaller.cs index f64c707..6a4efde 100644 --- a/WotBlitzStatisticsPro.Logic/WotBlitzStatisticsLogicInstaller.cs +++ b/WotBlitzStatisticsPro.Logic/WotBlitzStatisticsLogicInstaller.cs @@ -61,6 +61,7 @@ namespace WotBlitzStatisticsPro.Logic { services.AddTransient(); services.AddTransient(); + services.AddTransient(); services.AddTransient(serviceProvider => new ServiceProviderOperationFactory(serviceProvider)); diff --git a/WotBlitzStatisticsPro.WgApiClient/Model/WotAccountTanksStatistics.cs b/WotBlitzStatisticsPro.WgApiClient/Model/WotAccountTanksStatistics.cs index 33128a7..16e9efa 100644 --- a/WotBlitzStatisticsPro.WgApiClient/Model/WotAccountTanksStatistics.cs +++ b/WotBlitzStatisticsPro.WgApiClient/Model/WotAccountTanksStatistics.cs @@ -19,7 +19,7 @@ namespace WotBlitzStatisticsPro.WgApiClient.Model /// Last battle /// [JsonProperty("last_battle_time")] - private int LastBattleTime { get; set; } + public int LastBattleTime { get; set; } [JsonProperty("in_garage")] public bool? InGarage { get; set; }