Skip to content
This repository has been archived by the owner on Apr 8, 2024. It is now read-only.

Commit

Permalink
Merge pull request #358 from dragonfruitnetwork/ranked2-stats
Browse files Browse the repository at this point in the history
Add ranked2 stats
  • Loading branch information
aspriddell committed Dec 28, 2022
2 parents e53b6d4 + aaf327c commit ec8f06d
Show file tree
Hide file tree
Showing 11 changed files with 200 additions and 41 deletions.
10 changes: 9 additions & 1 deletion DragonFruit.Six.Api.Tests/Data/SeasonalStatsTests.cs
Expand Up @@ -3,6 +3,7 @@

using System.Linq;
using System.Threading.Tasks;
using DragonFruit.Six.Api.Enums;
using DragonFruit.Six.Api.Seasonal;
using DragonFruit.Six.Api.Seasonal.Enums;
using NUnit.Framework;
Expand Down Expand Up @@ -31,7 +32,7 @@ public async Task TestSeasonalRecords(string userId, int season15Rank, int seaso
[Test]
public async Task TestMultiPlatformMultiVersionRanked()
{
var stats = await Client.GetSeasonalStatsRecordsAsync(Accounts, new[] { 28, 27, 26, 25 });
var stats = (await Client.GetSeasonalStatsRecordsAsync(Accounts, new[] { 28, 27, 26, 25 })).ToList();
Assert.Greater(stats.Count, 48);

var casualMMR = stats.Single(x => x.ProfileId == "14c01250-ef26-4a32-92ba-e04aa557d619" && x.Board == BoardType.Casual && x.SeasonId == 26).MMR;
Expand All @@ -40,5 +41,12 @@ public async Task TestMultiPlatformMultiVersionRanked()
var rankedWins = stats.Where(x => x.ProfileId == "45c0cccb-a1a8-4433-b3d8-52aaa40d16d2" && x.Board == BoardType.Ranked).OrderByDescending(x => x.SeasonId).First().Wins;
Assert.Greater(rankedWins, 5);
}

[Test]
public async Task TestRanked2SeasonStats()
{
var stats = (await Client.GetSeasonalStatsAsync(Accounts, PlatformGroup.PC)).ToList();
Assert.GreaterOrEqual(Accounts.Count() * 4, stats.Count);
}
}
}
14 changes: 14 additions & 0 deletions DragonFruit.Six.Api/Enums/PlatformGroup.cs
@@ -0,0 +1,14 @@
// Dragon6 API Copyright DragonFruit Network <inbox@dragonfruit.network>
// Licensed under Apache-2. Refer to the LICENSE file for more info

using System;

namespace DragonFruit.Six.Api.Enums
{
[Flags]
public enum PlatformGroup
{
PC = 1,
Console = 2
}
}
11 changes: 0 additions & 11 deletions DragonFruit.Six.Api/Modern/Enums/PlatformGroup.cs

This file was deleted.

25 changes: 25 additions & 0 deletions DragonFruit.Six.Api/Seasonal/Entities/Ranked2SeasonMatchStats.cs
@@ -0,0 +1,25 @@
// Dragon6 API Copyright DragonFruit Network <inbox@dragonfruit.network>
// Licensed under Apache-2. Refer to the LICENSE file for more info

using System;
using DragonFruit.Six.Api.Utils;
using Newtonsoft.Json;

namespace DragonFruit.Six.Api.Seasonal.Entities
{
[Serializable]
[JsonObject(MemberSerialization.OptIn)]
public class Ranked2SeasonMatchStats
{
[JsonProperty("wins")]
public int Wins { get; set; }

[JsonProperty("losses")]
public int Losses { get; set; }

[JsonProperty("abandons")]
public int Abandons { get; set; }

public float WL => RatioUtils.RatioOf(Wins, Losses + Abandons);
}
}
60 changes: 60 additions & 0 deletions DragonFruit.Six.Api/Seasonal/Entities/Ranked2SeasonStats.cs
@@ -0,0 +1,60 @@
// Dragon6 API Copyright DragonFruit Network <inbox@dragonfruit.network>
// Licensed under Apache-2. Refer to the LICENSE file for more info

using System;
using DragonFruit.Six.Api.Enums;
using DragonFruit.Six.Api.Seasonal.Enums;
using DragonFruit.Six.Api.Utils;
using Newtonsoft.Json;

namespace DragonFruit.Six.Api.Seasonal.Entities
{
[Serializable]
[JsonObject(MemberSerialization.OptIn)]
public class Ranked2SeasonStats
{
[JsonProperty("board_id")]
public BoardType Board { get; set; }

[JsonProperty("id")]
public string ProfileId { get; set; }

[JsonProperty("season_id")]
public int SeasonId { get; set; }

[JsonProperty("platform_family")]
public PlatformGroup Platform { get; set; }

[JsonProperty("rank")]
public int Rank { get; set; }

[JsonProperty("rank_points")]
public int RankPoints { get; set; }

[JsonProperty("top_rank_position")]
public int TopRankPosition { get; set; }

[JsonProperty("max_rank")]
public int MaxRank { get; set; }

[JsonProperty("max_rank_points")]
public int MaxRankPoints { get; set; }

public RankInfo RankInfo => Ranks.GetFromId(Rank);

public RankInfo MaxRankInfo => Ranks.GetFromId(MaxRank);

[JsonProperty("kills")]
public int Kills { get; set; }

[JsonProperty("deaths")]
public int Deaths { get; set; }

[JsonProperty("match_outcomes")]
public Ranked2SeasonMatchStats Matches { get; set; }

public float KD => RatioUtils.RatioOf(Kills, Deaths);

public override string ToString() => $"{Platform}/{Board}: {ProfileId}";
}
}
2 changes: 2 additions & 0 deletions DragonFruit.Six.Api/Seasonal/Entities/SeasonalStats.cs
Expand Up @@ -101,5 +101,7 @@ public class SeasonalStats
public RankInfo RankInfo => _rankInfo ??= Ranks.GetRank(Rank, SeasonId);
public RankInfo MaxRankInfo => _maxRankInfo ??= Ranks.GetRank(MaxRank, SeasonId);
public RankInfo MMRRankInfo => _mmrRankInfo ??= Ranks.GetRank((int)MMR, SeasonId, true);

public override string ToString() => $"S{SeasonId}/{Board}: {ProfileId}";
}
}
22 changes: 0 additions & 22 deletions DragonFruit.Six.Api/Seasonal/Entities/SeasonalStatsResponse.cs

This file was deleted.

4 changes: 2 additions & 2 deletions DragonFruit.Six.Api/Seasonal/Enums/BoardType.cs
Expand Up @@ -22,11 +22,11 @@ public enum BoardType
Casual = 2,

[EnumMember(Value = "pvp_warmup")]
Deathmatch = 4,
Warmup = 4,

[EnumMember(Value = "pvp_event")]
Event = 8,

All = Ranked | Casual | Deathmatch | Event
All = Ranked | Casual | Warmup | Event
}
}
4 changes: 2 additions & 2 deletions DragonFruit.Six.Api/Seasonal/Ranks.cs
Expand Up @@ -39,10 +39,10 @@ public static RankInfo GetRank(int identifier, int season = -1, bool isMMR = fal
>= 15 and <= 22 => RankingV2,

// season 23-27
<= 27 => RankingV3,
>= 23 and <= 27 => RankingV3,

// season 28- (incl. latest season identifier)
_ => RankingV4
-1 or _ => RankingV4
};

if (isMMR)
Expand Down
34 changes: 34 additions & 0 deletions DragonFruit.Six.Api/Seasonal/Requests/Ranked2StatsRequest.cs
@@ -0,0 +1,34 @@
// Dragon6 API Copyright DragonFruit Network <inbox@dragonfruit.network>
// Licensed under Apache-2. Refer to the LICENSE file for more info

using System.Collections.Generic;
using System.Linq;
using DragonFruit.Data;
using DragonFruit.Data.Parameters;
using DragonFruit.Six.Api.Accounts.Entities;
using DragonFruit.Six.Api.Accounts.Enums;
using DragonFruit.Six.Api.Enums;

namespace DragonFruit.Six.Api.Seasonal.Requests
{
public class Ranked2StatsRequest : UbiApiRequest
{
public override string Path => Platform.CrossPlatform.SpaceUrl(2) + "/title/r6s/skill/full_profiles";

protected override UbisoftService? RequiredTokenSource => UbisoftService.RainbowSixClient;

public Ranked2StatsRequest(IEnumerable<UbisoftAccount> accounts, PlatformGroup platforms)
{
Accounts = accounts;
PlatformGroups = platforms;
}

public IEnumerable<UbisoftAccount> Accounts { get; set; }

[QueryParameter("platform_families", EnumHandlingMode.StringLower)]
public PlatformGroup PlatformGroups { get; set; }

[QueryParameter("profile_ids", CollectionConversionMode.Concatenated)]
private IEnumerable<string> AccountIdentifiers => Accounts.Select(x => x.ProfileId);
}
}
55 changes: 52 additions & 3 deletions DragonFruit.Six.Api/Seasonal/SeasonStatsExtensions.cs
Expand Up @@ -6,6 +6,7 @@
using System.Threading;
using System.Threading.Tasks;
using DragonFruit.Six.Api.Accounts.Entities;
using DragonFruit.Six.Api.Enums;
using DragonFruit.Six.Api.Seasonal.Entities;
using DragonFruit.Six.Api.Seasonal.Enums;
using DragonFruit.Six.Api.Seasonal.Requests;
Expand All @@ -16,6 +17,54 @@ namespace DragonFruit.Six.Api.Seasonal
{
public static class SeasonStatsExtensions
{
/// <summary>
/// Get current season stats for the provided <see cref="UbisoftAccount"/>
/// </summary>
/// <param name="client">The <see cref="Dragon6Client"/> to use</param>
/// <param name="account">The <see cref="UbisoftAccount"/> to get stats for</param>
/// <param name="platforms">The <see cref="PlatformGroup"/>s to return stats for</param>
/// <param name="cancellation">Optional cancellation token</param>
/// <returns>The current season stats for the account returning Ranked, Casual, Deathmatch and Event stats as separate <see cref="Ranked2SeasonStats"/> objects</returns>
/// <remarks>
/// This extension uses a protected endpoint to access data, and as such a supported token is needed.
/// Ensure that your <see cref="Dragon6Client"/> implementation requests a token for the provided <see cref="UbisoftService"/> when called.
/// </remarks>
public static Task<IEnumerable<Ranked2SeasonStats>> GetSeasonalStatsAsync(this Dragon6Client client, UbisoftAccount account, PlatformGroup platforms = PlatformGroup.PC | PlatformGroup.Console, CancellationToken cancellation = default)
{
return GetSeasonalStatsAsync(client, account.Yield(), platforms, cancellation);
}

/// <summary>
/// Get current season stats for the provided <see cref="UbisoftAccount"/>s (crossplay compatible)
/// </summary>
/// <param name="client">The <see cref="Dragon6Client"/> to use</param>
/// <param name="accounts">The <see cref="UbisoftAccount"/>s to get stats for</param>
/// <param name="platforms">The <see cref="PlatformGroup"/>s to return stats for</param>
/// <param name="cancellation">Optional cancellation token</param>
/// <returns>The current season stats for each account. Ranked, Casual, Deathmatch and Event stats will be returned as a separate <see cref="Ranked2SeasonStats"/> object</returns>
/// <remarks>
/// This extension uses a protected endpoint to access data, and as such a supported token is needed.
/// Ensure that your <see cref="Dragon6Client"/> implementation requests a token for the provided <see cref="UbisoftService"/> when called.
/// </remarks>
public static async Task<IEnumerable<Ranked2SeasonStats>> GetSeasonalStatsAsync(this Dragon6Client client, IEnumerable<UbisoftAccount> accounts, PlatformGroup platforms = PlatformGroup.PC | PlatformGroup.Console, CancellationToken cancellation = default)
{
var request = new Ranked2StatsRequest(accounts, platforms);
var response = await client.PerformAsync<JObject>(request, cancellation).ConfigureAwait(false);

return response.SelectTokens("$..full_profiles[*]").Select(x =>
{
var children = x.Values();
var root = (JObject)children.First();
foreach (var other in children.Skip(1).Cast<JObject>())
{
root.Merge(other);
}
return root.ToObject<Ranked2SeasonStats>();
});
}

/// <summary>
/// Get seasonal stats "records" for the provided <see cref="UbisoftAccount"/>s
/// </summary>
Expand All @@ -29,7 +78,7 @@ public static class SeasonStatsExtensions
/// This call is able to return results for multiple accounts spanning large numbers of seasons, ranking boards and regions.
/// Elements are returned ungrouped and unordered - it is recommended to sort then convert the returned <see cref="IEnumerable{T}"/> to an array or list afterwards.
/// </remarks>
public static Task<IReadOnlyCollection<SeasonalStats>> GetSeasonalStatsRecordsAsync(this Dragon6Client client, UbisoftAccount account, IEnumerable<int> seasonIds, BoardType? boards = null, Region? regions = null, CancellationToken token = default)
public static Task<IEnumerable<SeasonalStats>> GetSeasonalStatsRecordsAsync(this Dragon6Client client, UbisoftAccount account, IEnumerable<int> seasonIds, BoardType? boards = null, Region? regions = null, CancellationToken token = default)
{
return GetSeasonalStatsRecordsAsync(client, account.Yield(), seasonIds, boards, regions, token);
}
Expand All @@ -47,7 +96,7 @@ public static Task<IReadOnlyCollection<SeasonalStats>> GetSeasonalStatsRecordsAs
/// This call is able to return results for multiple accounts spanning large numbers of seasons, ranking boards and regions.
/// Elements are returned ungrouped and unordered - It is recommended to sort then convert the returned <see cref="IEnumerable{T}"/> to an array or list afterwards.
/// </remarks>
public static async Task<IReadOnlyCollection<SeasonalStats>> GetSeasonalStatsRecordsAsync(this Dragon6Client client, IEnumerable<UbisoftAccount> accounts, IEnumerable<int> seasonIds, BoardType? boards = null, Region? regions = null, CancellationToken token = default)
public static async Task<IEnumerable<SeasonalStats>> GetSeasonalStatsRecordsAsync(this Dragon6Client client, IEnumerable<UbisoftAccount> accounts, IEnumerable<int> seasonIds, BoardType? boards = null, Region? regions = null, CancellationToken token = default)
{
boards ??= BoardType.All;
seasonIds ??= (-1).Yield();
Expand Down Expand Up @@ -84,7 +133,7 @@ public static async Task<IReadOnlyCollection<SeasonalStats>> GetSeasonalStatsRec
var seasonalStatsRequests = requests.Select(x => client.PerformAsync<JObject>(x, token));
var seasonalStatsResponses = await Task.WhenAll(seasonalStatsRequests).ConfigureAwait(false);

return seasonalStatsResponses.SelectMany(x => x.SelectTokens("$..players_skill_records[*]")).Select(x => x.ToObject<SeasonalStats>()).ToList();
return seasonalStatsResponses.SelectMany(x => x.SelectTokens("$..players_skill_records[*]")).Select(x => x.ToObject<SeasonalStats>());
}
}
}

0 comments on commit ec8f06d

Please sign in to comment.