diff --git a/WotBlitzStatisticsPro.Common/Dictionaries/VehiclePriceInXp.cs b/WotBlitzStatisticsPro.Common/Dictionaries/VehiclePriceInXp.cs
new file mode 100644
index 0000000..a207dd8
--- /dev/null
+++ b/WotBlitzStatisticsPro.Common/Dictionaries/VehiclePriceInXp.cs
@@ -0,0 +1,18 @@
+namespace WotBlitzStatisticsPro.Common.Dictionaries
+{
+ ///
+ /// Price of vehicle
+ ///
+ public class VehiclePriceInXp
+ {
+ ///
+ /// VehicleId
+ ///
+ public long TankId { get; set; }
+
+ ///
+ /// Vehicle price in XP
+ ///
+ public long PricesXp { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/WotBlitzStatisticsPro.Common/Dictionaries/VehiclesDictionary.cs b/WotBlitzStatisticsPro.Common/Dictionaries/VehiclesDictionary.cs
new file mode 100644
index 0000000..4355ffd
--- /dev/null
+++ b/WotBlitzStatisticsPro.Common/Dictionaries/VehiclesDictionary.cs
@@ -0,0 +1,96 @@
+using System.Collections.Generic;
+
+namespace WotBlitzStatisticsPro.Common.Dictionaries
+{
+ ///
+ /// Vehicle dictionary item
+ ///
+ public class VehiclesDictionary
+ {
+ ///
+ /// VehicleId
+ ///
+ public long TankId { get; set; }
+
+ ///
+ /// Vehicle name
+ ///
+ public List Name { get; set; }
+
+ ///
+ /// Vehicle description
+ ///
+ public List Description { get; set; }
+
+ ///
+ /// Is vehicle premium
+ ///
+ public bool IsPremium { get; set; }
+
+ ///
+ /// Vehicle type id
+ ///
+ public string TypeId { get; set; }
+
+ ///
+ /// Vehicle nationId
+ ///
+ public string NationId { get; set; }
+
+ ///
+ /// Vehicle tier
+ ///
+ public int Tier { get; set; }
+
+ ///
+ /// Vehicle preview image
+ ///
+ public string PreviewImage { get; set; }
+
+ ///
+ /// Vehicle normal image
+ ///
+ public string NormalImage { get; set; }
+
+ ///
+ /// Vehicle price in credits
+ ///
+ public decimal PriceCredit { get; set; }
+
+ ///
+ /// Vehicle price in gold
+ ///
+ public decimal PriceGold { get; set; }
+
+ ///
+ /// Ids of nex tanks and their cost in XP
+ ///
+ public List NexTanksInTree { get; set; }
+
+ ///
+ /// Price in XP for parent vehicle as key
+ ///
+ public List PriceXp { get; set; }
+
+ ///
+ /// Engines id list
+ ///
+ public int[] Engines { get; set; }
+
+ ///
+ /// Guns id list
+ ///
+ public int[] Guns { get; set; }
+
+ ///
+ /// List of compatible suspensions
+ ///
+ public int[] Suspensions { get; set; }
+
+ ///
+ /// List of compatible turrets ids
+ ///
+ public int[] Turrets { get; set; }
+
+ }
+}
\ No newline at end of file
diff --git a/WotBlitzStatisticsPro.DataAccess/DictionariesDataAccessor.cs b/WotBlitzStatisticsPro.DataAccess/DictionariesDataAccessor.cs
index b7bd5f0..aa3c317 100644
--- a/WotBlitzStatisticsPro.DataAccess/DictionariesDataAccessor.cs
+++ b/WotBlitzStatisticsPro.DataAccess/DictionariesDataAccessor.cs
@@ -14,6 +14,7 @@ namespace WotBlitzStatisticsPro.DataAccess
private const string ClanRolesCollectionName = "dictionary-clan-roles";
private const string AchievementsSectionsCollectionName = "dictionary-achievements-sections";
private const string AchievementsCollectionName = "dictionary-achievements";
+ private const string VehiclesCollectionName = "dictionary-vehicles";
private readonly IMongoDatabase _database;
@@ -72,5 +73,14 @@ namespace WotBlitzStatisticsPro.DataAccess
await dbAchievementSections.DeleteManyAsync(Builders.Filter.Empty);
await dbAchievementSections.InsertManyAsync(achievementDictionary);
}
+
+ public async Task UpdateVehicles(List vehiclesDictionary)
+ {
+ var dbVehicles =
+ _database.GetCollection(VehiclesCollectionName);
+
+ await dbVehicles.DeleteManyAsync(Builders.Filter.Empty);
+ await dbVehicles.InsertManyAsync(vehiclesDictionary);
+ }
}
}
\ No newline at end of file
diff --git a/WotBlitzStatisticsPro.DataAccess/IDictionariesDataAccessor.cs b/WotBlitzStatisticsPro.DataAccess/IDictionariesDataAccessor.cs
index add465b..01e3649 100644
--- a/WotBlitzStatisticsPro.DataAccess/IDictionariesDataAccessor.cs
+++ b/WotBlitzStatisticsPro.DataAccess/IDictionariesDataAccessor.cs
@@ -17,5 +17,7 @@ namespace WotBlitzStatisticsPro.DataAccess
Task UpdateAchievementsSections(List achievementSections);
Task UpdateAchievements(List achievementDictionary);
+
+ Task UpdateVehicles(List vehiclesDictionary);
}
}
\ No newline at end of file
diff --git a/WotBlitzStatisticsPro.Logic/Dictionaries/VehiclesDictionaryUpdater.cs b/WotBlitzStatisticsPro.Logic/Dictionaries/VehiclesDictionaryUpdater.cs
index 299c601..40716ed 100644
--- a/WotBlitzStatisticsPro.Logic/Dictionaries/VehiclesDictionaryUpdater.cs
+++ b/WotBlitzStatisticsPro.Logic/Dictionaries/VehiclesDictionaryUpdater.cs
@@ -1,9 +1,13 @@
using System;
+using System.Collections.Generic;
+using System.Linq;
using System.Threading.Tasks;
using AutoMapper;
+using WotBlitzStatisticsPro.Common.Dictionaries;
using WotBlitzStatisticsPro.Common.Model;
using WotBlitzStatisticsPro.DataAccess;
using WotBlitzStatisticsPro.WgApiClient;
+using WotBlitzStatisticsPro.WgApiClient.Model;
namespace WotBlitzStatisticsPro.Logic.Dictionaries
{
@@ -16,19 +20,58 @@ namespace WotBlitzStatisticsPro.Logic.Dictionaries
[Obsolete("Parameter-less constructor only for unit tests")]
public VehiclesDictionaryUpdater()
{
-
+
}
- public VehiclesDictionaryUpdater(IWargamingDictionariesApiClient wargamingDictionariesApiClient, IDictionariesDataAccessor dataAccessor, IMapper mapper)
+ public VehiclesDictionaryUpdater(IWargamingDictionariesApiClient wargamingDictionariesApiClient,
+ IDictionariesDataAccessor dataAccessor, IMapper mapper)
{
_wargamingDictionariesApiClient = wargamingDictionariesApiClient;
_dataAccessor = dataAccessor;
_mapper = mapper;
}
- public virtual Task Update()
+ public virtual async Task Update()
{
- throw new System.NotImplementedException();
+ var vehiclesDictionary = await GetAndMapVehiclesDictionary();
+
+ await _dataAccessor.UpdateVehicles(vehiclesDictionary);
+
+ return new UpdateDictionariesResponseItem
+ {
+ DictionaryType = DictionaryType.Vehicles,
+ Description = $"Got and saved {vehiclesDictionary.Count} vehicles dictionary items"
+ };
+
}
+
+ private async Task> GetAndMapVehiclesDictionary()
+ {
+ var defaultRealmType = RealmType.Eu;
+
+ var vehicles = new List();
+
+ foreach (var requestLanguage in (RequestLanguage[]) Enum.GetValues(typeof(RequestLanguage)))
+ {
+ var wgVehicles =
+ await _wargamingDictionariesApiClient.GetVehicles(defaultRealmType, requestLanguage);
+
+ foreach (var wgVehicle in wgVehicles)
+ {
+ var mappedVehicle = vehicles.FirstOrDefault(v => v.TankId == wgVehicle.TankId);
+ if (mappedVehicle == null)
+ {
+ mappedVehicle = _mapper.Map(wgVehicle);
+ vehicles.Add(mappedVehicle);
+ }
+ mappedVehicle.Name.Add(new LocalizableString {Language = requestLanguage, Value = wgVehicle.Name});
+ mappedVehicle.Description.Add(new LocalizableString {Language = requestLanguage, Value = wgVehicle.Description});
+ }
+
+ }
+
+ return vehicles;
+ }
+
}
}
\ No newline at end of file
diff --git a/WotBlitzStatisticsPro.Logic/Mappers/VehicleDictionaryProfile.cs b/WotBlitzStatisticsPro.Logic/Mappers/VehicleDictionaryProfile.cs
new file mode 100644
index 0000000..4579462
--- /dev/null
+++ b/WotBlitzStatisticsPro.Logic/Mappers/VehicleDictionaryProfile.cs
@@ -0,0 +1,39 @@
+using System.Collections.Generic;
+using AutoMapper;
+using WotBlitzStatisticsPro.Common.Dictionaries;
+using WotBlitzStatisticsPro.WgApiClient.Model;
+
+namespace WotBlitzStatisticsPro.Logic.Mappers
+{
+ public class VehicleDictionaryProfile : Profile
+ {
+ public VehicleDictionaryProfile()
+ {
+ CreateMap()
+ .ForMember(d => d.TankId,
+ o => o.MapFrom(s => s.TankId))
+ .ForMember(d => d.Name, o => o.MapFrom(_ => new List()))
+ .ForMember(d => d.Description, o => o.MapFrom(_ => new List()))
+ .ForMember(d => d.TypeId,
+ o => o.MapFrom(s => s.Type))
+ .ForMember(d => d.NationId,
+ o => o.MapFrom(s => s.Nation))
+ .ForMember(d => d.Tier,
+ o => o.MapFrom(s => s.Tier))
+ .ForMember(d => d.PreviewImage,
+ o => o.MapFrom(s => s.Images["preview"]))
+ .ForMember(d => d.NormalImage,
+ o => o.MapFrom(s => s.Images["normal"]))
+ .ForMember(d => d.PriceCredit,
+ o => o.MapFrom(s => s.Cost["price_credit"]))
+ .ForMember(d => d.PriceGold,
+ o => o.MapFrom(s => s.Cost["price_gold"]))
+ .ForMember(d => d.NexTanksInTree,
+ o =>
+ o.MapFrom(s => s.NextTanks))
+ .ForMember(d => d.PriceXp,
+ o =>
+ o.MapFrom(s => s.PricesXp));
+ }
+ }
+}
\ No newline at end of file
diff --git a/WotBlitzStatisticsPro.Logic/Mappers/VehiclePriceProfile.cs b/WotBlitzStatisticsPro.Logic/Mappers/VehiclePriceProfile.cs
new file mode 100644
index 0000000..1602e82
--- /dev/null
+++ b/WotBlitzStatisticsPro.Logic/Mappers/VehiclePriceProfile.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using AutoMapper;
+using WotBlitzStatisticsPro.Common.Dictionaries;
+
+namespace WotBlitzStatisticsPro.Logic.Mappers
+{
+ public class VehiclePriceProfile : Profile
+ {
+ public VehiclePriceProfile()
+ {
+ CreateMap, VehiclePriceInXp>()
+ .ForMember(d => d.TankId,
+ o => o.MapFrom(s => Convert.ToInt64(s.Key)))
+ .ForMember(d => d.PricesXp,
+ o => o.MapFrom(s => Convert.ToInt64(s.Value)));
+ }
+ }
+}
\ No newline at end of file
diff --git a/WotBlitzStatisticsPro.Tests/DictionariesTests/VehiclesDictionaryUpdaterTests.cs b/WotBlitzStatisticsPro.Tests/DictionariesTests/VehiclesDictionaryUpdaterTests.cs
new file mode 100644
index 0000000..f7eeda0
--- /dev/null
+++ b/WotBlitzStatisticsPro.Tests/DictionariesTests/VehiclesDictionaryUpdaterTests.cs
@@ -0,0 +1,163 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using AutoMapper;
+using AutoMapper.Configuration;
+using Bogus;
+using FluentAssertions;
+using Moq;
+using NUnit.Framework;
+using WotBlitzStatisticsPro.Common.Dictionaries;
+using WotBlitzStatisticsPro.Common.Model;
+using WotBlitzStatisticsPro.DataAccess;
+using WotBlitzStatisticsPro.Logic.Dictionaries;
+using WotBlitzStatisticsPro.Logic.Mappers;
+using WotBlitzStatisticsPro.WgApiClient;
+using WotBlitzStatisticsPro.WgApiClient.Model;
+
+namespace WotBlitzStatisticsPro.Tests.DictionariesTests
+{
+ [TestFixture]
+ public class VehiclesDictionaryUpdaterTests
+ {
+ private VehiclesDictionaryUpdater _vehiclesDictionariesUpdater;
+ private Mock _wargamingDictionariesApiClientMock;
+ private Mock _dataAccessorMock;
+
+ private List _vehiclesInfoResponseEn;
+ private List _vehiclesInfoResponseRu;
+ private List _vehiclesInfoResponseDe;
+
+ [SetUp]
+ public void Init()
+ {
+ // Generating fake data
+ _vehiclesInfoResponseEn = GetVehiclesInfoResponse("en");
+ _vehiclesInfoResponseRu = GetVehiclesInfoResponse("ru", _vehiclesInfoResponseEn);
+ _vehiclesInfoResponseDe = GetVehiclesInfoResponse("de", _vehiclesInfoResponseEn);
+
+
+ // Real mapper instance
+ var configurationExp = new MapperConfigurationExpression();
+ configurationExp.AddProfile();
+ configurationExp.AddProfile();
+ var mapperConfiguration = new MapperConfiguration(configurationExp);
+ IMapper mapper = new Mapper(mapperConfiguration);
+
+ // Wargaming client mock
+ _wargamingDictionariesApiClientMock = new Mock();
+ _wargamingDictionariesApiClientMock.Setup(w =>
+ w.GetVehicles(It.IsAny(), RequestLanguage.En))
+ .ReturnsAsync(_vehiclesInfoResponseEn);
+ _wargamingDictionariesApiClientMock.Setup(w =>
+ w.GetVehicles(It.IsAny(), RequestLanguage.Ru))
+ .ReturnsAsync(_vehiclesInfoResponseRu);
+ _wargamingDictionariesApiClientMock.Setup(w =>
+ w.GetVehicles(It.IsAny(), RequestLanguage.De))
+ .ReturnsAsync(_vehiclesInfoResponseDe);
+
+ // MongoDb data accessor mock
+ _dataAccessorMock = new Mock();
+
+ _vehiclesDictionariesUpdater = new VehiclesDictionaryUpdater(
+ _wargamingDictionariesApiClientMock.Object,
+ _dataAccessorMock.Object,
+ mapper);
+
+ }
+
+ [Test]
+ public async Task ShouldCallWargamingApi3Times()
+ {
+ await _vehiclesDictionariesUpdater.Update();
+
+ // Should call wargaming API once for each language
+ _wargamingDictionariesApiClientMock.Verify(d => d.GetVehicles(It.IsAny(), RequestLanguage.En), Times.Once);
+ _wargamingDictionariesApiClientMock.Verify(d => d.GetVehicles(It.IsAny(), RequestLanguage.Ru), Times.Once);
+ _wargamingDictionariesApiClientMock.Verify(d => d.GetVehicles(It.IsAny(), RequestLanguage.De), Times.Once);
+ }
+
+ [Test]
+ public async Task ShouldSaveAppropriateVehiclesDictionary()
+ {
+ List targetVehicleDictionary = null;
+ _dataAccessorMock
+ .Setup(d => d.UpdateVehicles(It.IsAny>()))
+ .Callback((List dictionary) => targetVehicleDictionary = dictionary);
+
+ await _vehiclesDictionariesUpdater.Update();
+
+ targetVehicleDictionary.Should().NotBeNull();
+ targetVehicleDictionary.Should().HaveCount(targetVehicleDictionary.Count);
+
+ for (var i = 0; i < targetVehicleDictionary.Count; i++)
+ {
+ targetVehicleDictionary[i].TankId.Should()
+ .Be(_vehiclesInfoResponseEn[i].TankId);
+ targetVehicleDictionary[i].Name.First(n => n.Language == RequestLanguage.En).Value.Should()
+ .Be(_vehiclesInfoResponseEn[i].Name);
+ targetVehicleDictionary[i].Name.First(n => n.Language == RequestLanguage.Ru).Value.Should()
+ .Be(_vehiclesInfoResponseRu[i].Name);
+ targetVehicleDictionary[i].Name.First(n => n.Language == RequestLanguage.De).Value.Should()
+ .Be(_vehiclesInfoResponseDe[i].Name);
+ targetVehicleDictionary[i].Description.First(n => n.Language == RequestLanguage.En).Value.Should()
+ .Be(_vehiclesInfoResponseEn[i].Description);
+ targetVehicleDictionary[i].Description.First(n => n.Language == RequestLanguage.Ru).Value.Should()
+ .Be(_vehiclesInfoResponseRu[i].Description);
+ targetVehicleDictionary[i].Description.First(n => n.Language == RequestLanguage.De).Value.Should()
+ .Be(_vehiclesInfoResponseDe[i].Description);
+ }
+
+ }
+
+
+ #region Fixtures
+
+ private static List GetVehiclesInfoResponse(string language, List baseEncyclopedia = null)
+ {
+ var result = new List();
+ var faker = new Faker(language);
+
+ if (baseEncyclopedia == null)
+ {
+ var random = faker.Random;
+ for (int i = 0; i < random.Number(400); i++)
+ {
+ result.Add(FakeNewDictionaryItem(faker));
+ }
+ }
+ else
+ {
+ foreach (var achievementsResponse in baseEncyclopedia)
+ {
+ result.Add(FakeDictionaryItem(faker, achievementsResponse));
+ }
+ }
+
+ return result;
+ }
+
+ private static WotEncyclopediaVehiclesResponse FakeNewDictionaryItem(Faker faker)
+ {
+ return new WotEncyclopediaVehiclesResponse
+ {
+ Name = faker.Commerce.ProductName(),
+ TankId = faker.Random.Number(9999),
+ Description = faker.Lorem.Sentence(),
+ };
+ }
+
+ private static WotEncyclopediaVehiclesResponse FakeDictionaryItem(Faker faker, WotEncyclopediaVehiclesResponse baseResponse)
+ {
+ return new WotEncyclopediaVehiclesResponse
+ {
+ Name = faker.Commerce.ProductName(),
+ Description = faker.Lorem.Sentence(),
+ TankId = baseResponse.TankId
+ };
+ }
+
+ #endregion
+
+ }
+}
\ No newline at end of file
diff --git a/WotBlitzStatisticsPro.WgApiClient/IWargamingDictionariesApiClient.cs b/WotBlitzStatisticsPro.WgApiClient/IWargamingDictionariesApiClient.cs
index af3260a..42a953b 100644
--- a/WotBlitzStatisticsPro.WgApiClient/IWargamingDictionariesApiClient.cs
+++ b/WotBlitzStatisticsPro.WgApiClient/IWargamingDictionariesApiClient.cs
@@ -16,5 +16,9 @@ namespace WotBlitzStatisticsPro.WgApiClient
Task> GetAchievementsDictionary(
RealmType realmType = RealmType.Ru,
RequestLanguage language = RequestLanguage.En);
+
+ Task> GetVehicles(
+ RealmType realmType = RealmType.Ru,
+ RequestLanguage language = RequestLanguage.En);
}
}
\ No newline at end of file
diff --git a/WotBlitzStatisticsPro.WgApiClient/Model/WotEncyclopediaVehiclesResponse.cs b/WotBlitzStatisticsPro.WgApiClient/Model/WotEncyclopediaVehiclesResponse.cs
new file mode 100644
index 0000000..a1ab26e
--- /dev/null
+++ b/WotBlitzStatisticsPro.WgApiClient/Model/WotEncyclopediaVehiclesResponse.cs
@@ -0,0 +1,117 @@
+using System.Collections.Generic;
+using Newtonsoft.Json;
+
+namespace WotBlitzStatisticsPro.WgApiClient.Model
+{
+ public class WotEncyclopediaVehiclesResponse
+ {
+ ///
+ /// Vehicle description
+ ///
+ [JsonProperty("description")]
+ public string Description { get; set; }
+
+ ///
+ /// Engines id list
+ ///
+ [JsonProperty("engines")]
+ public int[] Engines { get; set; }
+
+ ///
+ /// Guns id list
+ ///
+ [JsonProperty("guns")]
+ public int[] Guns { get; set; }
+
+ ///
+ /// Is vehicle premium
+ ///
+ [JsonProperty("is_premium")]
+ public bool IsPremium { get; set; }
+
+ ///
+ /// Vehicle name
+ ///
+ [JsonProperty("name")]
+ public string Name { get; set; }
+
+ ///
+ /// Vehicle nationId
+ ///
+ [JsonProperty("nation")]
+ public string Nation { get; set; }
+
+ ///
+ /// List of next vehicles in tree:
+ ///
+ /// key - vehicle id,
+ /// value - amount of XP to open it
+ ///
+ [JsonProperty("next_tanks")]
+ public Dictionary NextTanks { get; set; }
+
+ ///
+ /// Vehicle cost by pairs
+ /// with keys: price_credit, price_gold
+ ///
+ [JsonProperty("cost")]
+ public Dictionary Cost { get; set; }
+
+ ///
+ /// Price in XP for parent vehicle as key
+ ///
+ [JsonProperty("prices_xp")]
+ public Dictionary PricesXp { get; set; }
+
+ ///
+ /// List of compatible suspensions
+ ///
+ [JsonProperty("suspensions")]
+ public int[] Suspensions { get; set; }
+
+ ///
+ /// VehicleId
+ ///
+ [JsonProperty("tank_id")]
+ public long? TankId { get; set; }
+
+ ///
+ /// Vehicle tier
+ ///
+ [JsonProperty("tier")]
+ public long? Tier { get; set; }
+
+ ///
+ /// List of compatible turrets ids
+ ///
+ [JsonProperty("turrets")]
+ public int[] Turrets { get; set; }
+
+ ///
+ /// Vehicle type id
+ ///
+ [JsonProperty("type")]
+ public string Type { get; set; }
+
+ /////
+ ///// Default vehicle profile
+ /////
+ //[JsonProperty("default_profile")]
+ //public WotEncyclopediaVehiclesDefaultProfile DefaultProfile { get; set; }
+
+ ///
+ /// List of vehicle images.
+ /// Keys: preview, normal
+ ///
+ [JsonProperty("images")]
+ public Dictionary Images { get; set; }
+
+ /////
+ ///// Information about investigated modules
+ /////
+ //[JsonProperty("modules_tree")]
+ //public WotEncyclopediaVehiclesModulesTree ModulesTree { get; set; }
+ }
+
+
+}
\ No newline at end of file
diff --git a/WotBlitzStatisticsPro.WgApiClient/WargamingApiClient.cs b/WotBlitzStatisticsPro.WgApiClient/WargamingApiClient.cs
index 9c825a6..32c1621 100644
--- a/WotBlitzStatisticsPro.WgApiClient/WargamingApiClient.cs
+++ b/WotBlitzStatisticsPro.WgApiClient/WargamingApiClient.cs
@@ -76,6 +76,16 @@ namespace WotBlitzStatisticsPro.WgApiClient
return response.Values.ToList();
}
+ public async Task> GetVehicles(RealmType realmType = RealmType.Ru, RequestLanguage language = RequestLanguage.En)
+ {
+ var response = await GetFromBlitzApi>(
+ realmType,
+ language,
+ "encyclopedia/vehicles/").ConfigureAwait(false);
+
+ return response.Values.ToList();
+ }
+
#endregion