From 47083c54b33e26271a9213d2937f0a5097a77af9 Mon Sep 17 00:00:00 2001 From: Albie Date: Thu, 12 Jan 2023 16:12:03 +0000 Subject: [PATCH 1/7] create season info struct --- .../Modern/Utils/ModernSeason.cs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 DragonFruit.Six.Api/Modern/Utils/ModernSeason.cs diff --git a/DragonFruit.Six.Api/Modern/Utils/ModernSeason.cs b/DragonFruit.Six.Api/Modern/Utils/ModernSeason.cs new file mode 100644 index 00000000..256ba24f --- /dev/null +++ b/DragonFruit.Six.Api/Modern/Utils/ModernSeason.cs @@ -0,0 +1,23 @@ +// Dragon6 API Copyright DragonFruit Network +// Licensed under Apache-2. Refer to the LICENSE file for more info + +using System; + +namespace DragonFruit.Six.Api.Modern.Utils +{ + public readonly struct ModernSeason + { + public ModernSeason(int year, int season) + { + Year = year; + Season = Math.Max(season, 4); + } + + public int Year { get; } + public int Season { get; } + + public int SeasonNumber => (Year - 1) * 4 + Season; + + public override string ToString() => $"Y{Year}S{Season}"; + } +} From 6639ac64d05caf95ba029dc053b4ab65996f645c Mon Sep 17 00:00:00 2001 From: Albie Date: Thu, 12 Jan 2023 16:12:14 +0000 Subject: [PATCH 2/7] update modern platform names --- DragonFruit.Six.Api/Modern/Utils/ModernPlatformUtils.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DragonFruit.Six.Api/Modern/Utils/ModernPlatformUtils.cs b/DragonFruit.Six.Api/Modern/Utils/ModernPlatformUtils.cs index 55492caf..4a13e8c8 100644 --- a/DragonFruit.Six.Api/Modern/Utils/ModernPlatformUtils.cs +++ b/DragonFruit.Six.Api/Modern/Utils/ModernPlatformUtils.cs @@ -14,8 +14,8 @@ public static class ModernPlatformUtils public static string ModernName(this Platform platform) => platform switch { Platform.PC => "PC", - Platform.PSN => "PSN", - Platform.XB1 => "XONE", + Platform.PSN => "PLAYSTATION", + Platform.XB1 => "XBOX", _ => throw new ArgumentOutOfRangeException() }; From 955c0473d7147dd3d21c9c7a33b376830eea66ad Mon Sep 17 00:00:00 2001 From: Albie Date: Thu, 12 Jan 2023 16:12:30 +0000 Subject: [PATCH 3/7] update platform properties --- .../Modern/ModernStatsDeserializer.cs | 9 +-- .../Modern/ModernStatsExtensions.cs | 2 +- .../Modern/Requests/ModernStatsRequest.cs | 62 ++++++++++++++++--- 3 files changed, 59 insertions(+), 14 deletions(-) diff --git a/DragonFruit.Six.Api/Modern/ModernStatsDeserializer.cs b/DragonFruit.Six.Api/Modern/ModernStatsDeserializer.cs index d718d568..8f35f57e 100644 --- a/DragonFruit.Six.Api/Modern/ModernStatsDeserializer.cs +++ b/DragonFruit.Six.Api/Modern/ModernStatsDeserializer.cs @@ -2,7 +2,6 @@ // Licensed under Apache-2. Refer to the LICENSE file for more info using DragonFruit.Six.Api.Modern.Containers; -using DragonFruit.Six.Api.Modern.Requests; using DragonFruit.Six.Api.Modern.Utils; using Newtonsoft.Json; using Newtonsoft.Json.Linq; @@ -14,10 +13,12 @@ public static class ModernStatsDeserializer /// /// Abstracts a ubisoft stats response to present the data required /// - public static ModernModeStatsContainer ProcessData(this JObject source, ModernStatsRequest request) + public static ModernModeStatsContainer ProcessData(this JObject source) { - var platformKey = request.PlatformGroup?.ToString().ToUpperInvariant() ?? request.Account.Platform.ModernName(); - return source?["platforms"]?[platformKey]?.ToObject>(new JsonSerializer + var container = (JProperty)source?["profileData"].First; + var platformContainer = (JProperty)container?.Value["platforms"]!.First; + + return platformContainer?.Value.ToObject>(new JsonSerializer { Converters = { new JsonPathConverter() } }); diff --git a/DragonFruit.Six.Api/Modern/ModernStatsExtensions.cs b/DragonFruit.Six.Api/Modern/ModernStatsExtensions.cs index a18da794..7f1ffce2 100644 --- a/DragonFruit.Six.Api/Modern/ModernStatsExtensions.cs +++ b/DragonFruit.Six.Api/Modern/ModernStatsExtensions.cs @@ -29,7 +29,7 @@ public static class ModernStatsExtensions public static async Task> GetModernStatsAsync(this Dragon6Client client, ModernStatsRequest request, CancellationToken token = default) { var response = await client.PerformAsync(request, token).ConfigureAwait(false); - return response.ProcessData(request); + return response.ProcessData(); } /// diff --git a/DragonFruit.Six.Api/Modern/Requests/ModernStatsRequest.cs b/DragonFruit.Six.Api/Modern/Requests/ModernStatsRequest.cs index 1505106e..54a8eddb 100644 --- a/DragonFruit.Six.Api/Modern/Requests/ModernStatsRequest.cs +++ b/DragonFruit.Six.Api/Modern/Requests/ModernStatsRequest.cs @@ -3,30 +3,43 @@ using System; using System.Collections.Generic; +using System.Linq; using DragonFruit.Data; using DragonFruit.Data.Parameters; +using DragonFruit.Data.Requests; using DragonFruit.Six.Api.Accounts.Entities; +using DragonFruit.Six.Api.Accounts.Enums; using DragonFruit.Six.Api.Enums; using DragonFruit.Six.Api.Modern.Enums; using DragonFruit.Six.Api.Modern.Utils; +using DragonFruit.Six.Api.Seasonal.Requests; using JetBrains.Annotations; namespace DragonFruit.Six.Api.Modern.Requests { - public abstract class ModernStatsRequest : UbiApiRequest + public abstract class ModernStatsRequest : UbiApiRequest, IRequestExecutingCallback { private const string DateTimeFormat = "yyyyMMdd"; private const int DefaultStartWindow = 14; private PlaylistType? _playlist; private OperatorType? _operatorType; + private PlatformGroup? _platformGroup; private DateTimeOffset? _startDate, _endDate; + private readonly IList> _queries; + + private static readonly DateTimeOffset CrossPlatformStartDate = new(2022, 12, 6, 0, 0, 0, TimeSpan.Zero); + public override string Path => $"https://prod.datadev.ubisoft.com/v1/users/{Account.UbisoftId}/playerstats"; + protected override IEnumerable> AdditionalQueries => _queries; + protected ModernStatsRequest(UbisoftAccount account) { Account = account ?? throw new NullReferenceException(); + + _queries = new List>(1); } /// @@ -92,13 +105,16 @@ public virtual DateTimeOffset EndDate /// List of seasons to return stats for. Provides an alternative timespan compared to start-end dates. /// [QueryParameter("seasons", CollectionConversionMode.Concatenated)] - public virtual IEnumerable Seasons { get; set; } + public virtual IEnumerable Seasons { get; set; } /// /// Optional to override when getting cross-progression metrics /// - [QueryParameter("platformGroup", EnumHandlingMode.StringUpper)] - public PlatformGroup? PlatformGroup { get; set; } + public PlatformGroup PlatformGroup + { + get => _platformGroup ??= Account.Platform == Platform.PC ? PlatformGroup.PC : PlatformGroup.Console; + set => _platformGroup = value; + } /// /// The type of request (general, operators, weapons, etc.) @@ -117,11 +133,6 @@ public virtual DateTimeOffset EndDate [QueryParameter("view")] protected virtual string RequestCategory => "current"; - [CanBeNull] - [UsedImplicitly] - [QueryParameter("platform")] - protected string PlatformName => PlatformGroup.HasValue ? null : Account.Platform.ModernName(); - [UsedImplicitly] [QueryParameter("spaceId")] protected string GameSpaceId => Account.Platform.GameSpaceId(); @@ -143,5 +154,38 @@ public virtual DateTimeOffset EndDate [UsedImplicitly] [QueryParameter("teamRole")] protected virtual string OperatorTypeNames => OperatorType.Expand(); + + void IRequestExecutingCallback.OnRequestExecuting(ApiClient client) + { + bool useCrossPlayQueries; + + if (Seasons?.Any() != true) + { + useCrossPlayQueries = EndDate < CrossPlatformStartDate; + } + else + { + // check seasons to make sure old and new seasons aren't mixed in + if (Seasons.All(x => x.SeasonNumber < SeasonalStatsRecordRequest.CrossPlatformProgressionId)) + { + useCrossPlayQueries = false; + } + else if (Seasons.All(x => x.SeasonNumber >= SeasonalStatsRecordRequest.CrossPlatformProgressionId)) + { + useCrossPlayQueries = true; + } + else + { + throw new ArgumentException($"{nameof(Seasons)} combines both cross-platform and individual platform seasons.", nameof(Seasons)); + } + } + + var platformQuery = useCrossPlayQueries + ? new KeyValuePair("platform", Account.Platform.ModernName()) + : new KeyValuePair("platformGroup", PlatformGroup.ToString()); + + _queries.Clear(); + _queries.Add(platformQuery); + } } } From f83dfc67d2ba868e74f4f6c034a09500230f6dfa Mon Sep 17 00:00:00 2001 From: Albie Date: Thu, 12 Jan 2023 16:32:14 +0000 Subject: [PATCH 4/7] fix season requests not producing expected results --- .../Requests/ModernSeasonalStatsRequest.cs | 16 +++++++++++++++- .../Modern/Requests/ModernStatsRequest.cs | 19 +++++++------------ 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/DragonFruit.Six.Api/Modern/Requests/ModernSeasonalStatsRequest.cs b/DragonFruit.Six.Api/Modern/Requests/ModernSeasonalStatsRequest.cs index 094462cf..9b61fcc8 100644 --- a/DragonFruit.Six.Api/Modern/Requests/ModernSeasonalStatsRequest.cs +++ b/DragonFruit.Six.Api/Modern/Requests/ModernSeasonalStatsRequest.cs @@ -2,12 +2,16 @@ // Licensed under Apache-2. Refer to the LICENSE file for more info using System; +using System.Collections.Generic; +using DragonFruit.Data; +using DragonFruit.Data.Requests; using DragonFruit.Six.Api.Accounts.Entities; using DragonFruit.Six.Api.Enums; +using DragonFruit.Six.Api.Modern.Utils; namespace DragonFruit.Six.Api.Modern.Requests { - public class ModernSeasonalStatsRequest : ModernStatsRequest + public class ModernSeasonalStatsRequest : ModernStatsRequest, IRequestExecutingCallback { protected override string RequestCategory => "seasonal"; protected override string RequestType => "summary"; @@ -38,5 +42,15 @@ public override OperatorType OperatorType protected override string FormattedStartDate => null; protected override string FormattedEndDate => null; protected override string OperatorTypeNames => null; + + void IRequestExecutingCallback.OnRequestExecuting(ApiClient client) + { + var platformQuery = !PlatformGroup.HasValue + ? new KeyValuePair("platform", Account.Platform.ModernName()) + : new KeyValuePair("platformGroup", PlatformGroup.ToString()); + + Queries.Clear(); + Queries.Add(platformQuery); + } } } diff --git a/DragonFruit.Six.Api/Modern/Requests/ModernStatsRequest.cs b/DragonFruit.Six.Api/Modern/Requests/ModernStatsRequest.cs index 54a8eddb..532d2ce4 100644 --- a/DragonFruit.Six.Api/Modern/Requests/ModernStatsRequest.cs +++ b/DragonFruit.Six.Api/Modern/Requests/ModernStatsRequest.cs @@ -24,22 +24,21 @@ public abstract class ModernStatsRequest : UbiApiRequest, IRequestExecutingCallb private PlaylistType? _playlist; private OperatorType? _operatorType; - private PlatformGroup? _platformGroup; private DateTimeOffset? _startDate, _endDate; - private readonly IList> _queries; + protected readonly IList> Queries; private static readonly DateTimeOffset CrossPlatformStartDate = new(2022, 12, 6, 0, 0, 0, TimeSpan.Zero); public override string Path => $"https://prod.datadev.ubisoft.com/v1/users/{Account.UbisoftId}/playerstats"; - protected override IEnumerable> AdditionalQueries => _queries; + protected override IEnumerable> AdditionalQueries => Queries; protected ModernStatsRequest(UbisoftAccount account) { Account = account ?? throw new NullReferenceException(); - _queries = new List>(1); + Queries = new List>(1); } /// @@ -110,11 +109,7 @@ public virtual DateTimeOffset EndDate /// /// Optional to override when getting cross-progression metrics /// - public PlatformGroup PlatformGroup - { - get => _platformGroup ??= Account.Platform == Platform.PC ? PlatformGroup.PC : PlatformGroup.Console; - set => _platformGroup = value; - } + public PlatformGroup? PlatformGroup { get; set; } /// /// The type of request (general, operators, weapons, etc.) @@ -182,10 +177,10 @@ void IRequestExecutingCallback.OnRequestExecuting(ApiClient client) var platformQuery = useCrossPlayQueries ? new KeyValuePair("platform", Account.Platform.ModernName()) - : new KeyValuePair("platformGroup", PlatformGroup.ToString()); + : new KeyValuePair("platformGroup", (PlatformGroup ?? (Account.Platform == Platform.PC ? Api.Enums.PlatformGroup.PC : Api.Enums.PlatformGroup.Console)).ToString()); - _queries.Clear(); - _queries.Add(platformQuery); + Queries.Clear(); + Queries.Add(platformQuery); } } } From 03800041df9856210db85e33f5877b07fad19c1f Mon Sep 17 00:00:00 2001 From: Albie Date: Thu, 12 Jan 2023 16:32:29 +0000 Subject: [PATCH 5/7] fix multiple profiles being returned --- DragonFruit.Six.Api/Modern/ModernStatsDeserializer.cs | 7 +++---- DragonFruit.Six.Api/Modern/ModernStatsExtensions.cs | 8 +++++--- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/DragonFruit.Six.Api/Modern/ModernStatsDeserializer.cs b/DragonFruit.Six.Api/Modern/ModernStatsDeserializer.cs index 8f35f57e..d8aedbdd 100644 --- a/DragonFruit.Six.Api/Modern/ModernStatsDeserializer.cs +++ b/DragonFruit.Six.Api/Modern/ModernStatsDeserializer.cs @@ -1,6 +1,7 @@ // Dragon6 API Copyright DragonFruit Network // Licensed under Apache-2. Refer to the LICENSE file for more info +using DragonFruit.Six.Api.Accounts.Entities; using DragonFruit.Six.Api.Modern.Containers; using DragonFruit.Six.Api.Modern.Utils; using Newtonsoft.Json; @@ -13,11 +14,9 @@ public static class ModernStatsDeserializer /// /// Abstracts a ubisoft stats response to present the data required /// - public static ModernModeStatsContainer ProcessData(this JObject source) + public static ModernModeStatsContainer ProcessData(this JObject source, UbisoftAccount account) { - var container = (JProperty)source?["profileData"].First; - var platformContainer = (JProperty)container?.Value["platforms"]!.First; - + var platformContainer = (JProperty)source?["profileData"]![account.ProfileId]!["platforms"]!.First; return platformContainer?.Value.ToObject>(new JsonSerializer { Converters = { new JsonPathConverter() } diff --git a/DragonFruit.Six.Api/Modern/ModernStatsExtensions.cs b/DragonFruit.Six.Api/Modern/ModernStatsExtensions.cs index 7f1ffce2..31f809b4 100644 --- a/DragonFruit.Six.Api/Modern/ModernStatsExtensions.cs +++ b/DragonFruit.Six.Api/Modern/ModernStatsExtensions.cs @@ -29,7 +29,7 @@ public static class ModernStatsExtensions public static async Task> GetModernStatsAsync(this Dragon6Client client, ModernStatsRequest request, CancellationToken token = default) { var response = await client.PerformAsync(request, token).ConfigureAwait(false); - return response.ProcessData(); + return response.ProcessData(request.Account); } /// @@ -97,13 +97,15 @@ public static Task GetModernOperatorInfoAsync(this ApiClie /// The to use /// The to get stats for /// The to get stats for + /// Optional to get modern cross-platform stats for. /// Optional /// A container with all seasons tracked. Will return null if no stats found - public static Task>> GetModernSeasonStatsAsync(this Dragon6Client client, UbisoftAccount account, PlaylistType playlistType = PlaylistType.All, CancellationToken token = default) + public static Task>> GetModernSeasonStatsAsync(this Dragon6Client client, UbisoftAccount account, PlaylistType playlistType = PlaylistType.All, PlatformGroup? crossPlatformGroup = null, CancellationToken token = default) { var request = new ModernSeasonalStatsRequest(account) { - Playlist = playlistType + Playlist = playlistType, + PlatformGroup = crossPlatformGroup }; return client.GetModernStatsAsync>(request, token); From 35340beea68c37a20ad77bb928b5bb9027192673 Mon Sep 17 00:00:00 2001 From: Albie Date: Thu, 12 Jan 2023 16:35:25 +0000 Subject: [PATCH 6/7] fix wrong season logic --- DragonFruit.Six.Api/Modern/Requests/ModernStatsRequest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DragonFruit.Six.Api/Modern/Requests/ModernStatsRequest.cs b/DragonFruit.Six.Api/Modern/Requests/ModernStatsRequest.cs index 532d2ce4..b7a61ec8 100644 --- a/DragonFruit.Six.Api/Modern/Requests/ModernStatsRequest.cs +++ b/DragonFruit.Six.Api/Modern/Requests/ModernStatsRequest.cs @@ -156,7 +156,7 @@ void IRequestExecutingCallback.OnRequestExecuting(ApiClient client) if (Seasons?.Any() != true) { - useCrossPlayQueries = EndDate < CrossPlatformStartDate; + useCrossPlayQueries = EndDate > CrossPlatformStartDate; } else { From 1c3df3674acaeb822d544a562659953b72c4fa5f Mon Sep 17 00:00:00 2001 From: Albie Date: Thu, 12 Jan 2023 16:57:43 +0000 Subject: [PATCH 7/7] improve profile id matching --- .../Modern/ModernStatsDeserializer.cs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/DragonFruit.Six.Api/Modern/ModernStatsDeserializer.cs b/DragonFruit.Six.Api/Modern/ModernStatsDeserializer.cs index d8aedbdd..6e7c8a0a 100644 --- a/DragonFruit.Six.Api/Modern/ModernStatsDeserializer.cs +++ b/DragonFruit.Six.Api/Modern/ModernStatsDeserializer.cs @@ -16,8 +16,18 @@ public static class ModernStatsDeserializer /// public static ModernModeStatsContainer ProcessData(this JObject source, UbisoftAccount account) { - var platformContainer = (JProperty)source?["profileData"]![account.ProfileId]!["platforms"]!.First; - return platformContainer?.Value.ToObject>(new JsonSerializer + var profileContainer = source?["profileData"].ToObject(); + + if (profileContainer == null) + { + return null; + } + + var platformContainer = profileContainer.Count == 1 + ? profileContainer.First.Value().Value.ToObject() + : profileContainer[account.ProfileId]; + + return platformContainer?["platforms"]?.First.Value().Value.ToObject>(new JsonSerializer { Converters = { new JsonPathConverter() } });