From bd2851c3862624acc484cb0cd2db03d494061a92 Mon Sep 17 00:00:00 2001 From: Salman Alshamrani Date: Wed, 21 May 2025 17:29:29 +0300 Subject: [PATCH 1/2] Fix nominations count system not updated to newer API structure --- .../Beatmaps/BeatmapSetNominationStatus.cs | 4 +-- .../BeatmapSetNominationStatusRequiredMeta.cs | 25 +++++++++++++++++++ .../Cards/Statistics/NominationsStatistic.cs | 23 +++++++++++++---- 3 files changed, 45 insertions(+), 7 deletions(-) create mode 100644 osu.Game/Beatmaps/BeatmapSetNominationStatusRequiredMeta.cs diff --git a/osu.Game/Beatmaps/BeatmapSetNominationStatus.cs b/osu.Game/Beatmaps/BeatmapSetNominationStatus.cs index 8cf43ab320..267e46b0d4 100644 --- a/osu.Game/Beatmaps/BeatmapSetNominationStatus.cs +++ b/osu.Game/Beatmaps/BeatmapSetNominationStatus.cs @@ -19,7 +19,7 @@ namespace osu.Game.Beatmaps /// /// The number of nominations required so that the map is eligible for qualification. /// - [JsonProperty(@"required")] - public int Required { get; set; } + [JsonProperty(@"required_meta")] + public BeatmapSetNominationRequiredMeta RequiredMeta { get; set; } = new BeatmapSetNominationRequiredMeta(); } } diff --git a/osu.Game/Beatmaps/BeatmapSetNominationStatusRequiredMeta.cs b/osu.Game/Beatmaps/BeatmapSetNominationStatusRequiredMeta.cs new file mode 100644 index 0000000000..44d31d7b2c --- /dev/null +++ b/osu.Game/Beatmaps/BeatmapSetNominationStatusRequiredMeta.cs @@ -0,0 +1,25 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using Newtonsoft.Json; + +namespace osu.Game.Beatmaps +{ + /// + /// Contains information about the number of nominations required for a beatmap set. + /// + public class BeatmapSetNominationRequiredMeta + { + /// + /// The number of nominations required for difficulties of the main ruleset. + /// + [JsonProperty(@"main_ruleset")] + public int MainRuleset { get; set; } + + /// + /// The number of nominations required for difficulties of each non-main ruleset. + /// + [JsonProperty(@"non_main_ruleset")] + public int NonMainRuleset { get; set; } + } +} diff --git a/osu.Game/Beatmaps/Drawables/Cards/Statistics/NominationsStatistic.cs b/osu.Game/Beatmaps/Drawables/Cards/Statistics/NominationsStatistic.cs index 083f1a353b..01f6fde256 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/Statistics/NominationsStatistic.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/Statistics/NominationsStatistic.cs @@ -1,8 +1,10 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics.Sprites; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Resources.Localisation.Web; namespace osu.Game.Beatmaps.Drawables.Cards.Statistics @@ -12,16 +14,27 @@ namespace osu.Game.Beatmaps.Drawables.Cards.Statistics /// public partial class NominationsStatistic : BeatmapCardStatistic { - private NominationsStatistic(BeatmapSetNominationStatus nominationStatus) + private NominationsStatistic(int current, int required) { Icon = FontAwesome.Solid.ThumbsUp; - Text = nominationStatus.Current.ToLocalisableString(); - TooltipText = BeatmapsStrings.NominationsRequiredText(nominationStatus.Current.ToLocalisableString(), nominationStatus.Required.ToLocalisableString()); + Text = current.ToLocalisableString(); + TooltipText = BeatmapsStrings.NominationsRequiredText(current.ToLocalisableString(), required.ToLocalisableString()); } - public static NominationsStatistic? CreateFor(IBeatmapSetOnlineInfo beatmapSetOnlineInfo) + public static NominationsStatistic? CreateFor(APIBeatmapSet beatmapSet) + { // web does not show nominations unless hypes are also present. // see: https://github.com/ppy/osu-web/blob/8ed7d071fd1d3eaa7e43cf0e4ff55ca2fef9c07c/resources/assets/lib/beatmapset-panel.tsx#L443 - => beatmapSetOnlineInfo.HypeStatus == null || beatmapSetOnlineInfo.NominationStatus == null ? null : new NominationsStatistic(beatmapSetOnlineInfo.NominationStatus); + if (beatmapSet.HypeStatus == null || beatmapSet.NominationStatus == null) + return null; + + int current = beatmapSet.NominationStatus.Current; + int requiredMainRuleset = beatmapSet.NominationStatus.RequiredMeta.MainRuleset; + int requiredNonMainRuleset = beatmapSet.NominationStatus.RequiredMeta.NonMainRuleset; + + int rulesets = beatmapSet.Beatmaps.GroupBy(b => b.Ruleset).Count(); + + return new NominationsStatistic(current, requiredMainRuleset + requiredNonMainRuleset * (rulesets - 1)); + } } } From 17afea431b0ad69aab7f2f333a5563c8f9225d6c Mon Sep 17 00:00:00 2001 From: Salman Alshamrani Date: Wed, 21 May 2025 17:29:31 +0300 Subject: [PATCH 2/2] Add test coverage --- .../Visual/Beatmaps/TestSceneBeatmapCard.cs | 67 ++++++++++++++++++- 1 file changed, 64 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCard.cs b/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCard.cs index fed26d8acb..2f31911fac 100644 --- a/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCard.cs +++ b/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCard.cs @@ -16,6 +16,7 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Beatmaps.Drawables.Cards; using osu.Game.Beatmaps.Drawables.Cards.Buttons; +using osu.Game.Beatmaps.Drawables.Cards.Statistics; using osu.Game.Graphics.Containers; using osu.Game.Online.API; using osu.Game.Online.API.Requests; @@ -63,7 +64,11 @@ namespace osu.Game.Tests.Visual.Beatmaps withStatistics.NominationStatus = new BeatmapSetNominationStatus { Current = 1, - Required = 2 + RequiredMeta = + { + MainRuleset = 2, + NonMainRuleset = 1, + } }; var undownloadable = getUndownloadableBeatmapSet(); @@ -78,7 +83,11 @@ namespace osu.Game.Tests.Visual.Beatmaps someDifficulties.NominationStatus = new BeatmapSetNominationStatus { Current = 2, - Required = 2 + RequiredMeta = + { + MainRuleset = 2, + NonMainRuleset = 1, + } }; var manyDifficulties = getManyDifficultiesBeatmapSet(100); @@ -220,6 +229,9 @@ namespace osu.Game.Tests.Visual.Beatmaps } private Drawable createContent(OverlayColourScheme colourScheme, Func creationFunc) + => createContent(colourScheme, testCases.Select(creationFunc).ToArray()); + + private Drawable createContent(OverlayColourScheme colourScheme, Drawable[] cards) { var colourProvider = new OverlayColourProvider(colourScheme); @@ -247,7 +259,7 @@ namespace osu.Game.Tests.Visual.Beatmaps Direction = FillDirection.Full, Padding = new MarginPadding(10), Spacing = new Vector2(10), - ChildrenEnumerable = testCases.Select(creationFunc) + ChildrenEnumerable = cards } } } @@ -320,5 +332,54 @@ namespace osu.Game.Tests.Visual.Beatmaps BeatmapCardNormal firstCard() => this.ChildrenOfType().First(); } + + [Test] + public void TestNominations() + { + AddStep("create cards", () => + { + var singleRuleset = CreateAPIBeatmapSet(Ruleset.Value); + singleRuleset.HypeStatus = new BeatmapSetHypeStatus(); + singleRuleset.NominationStatus = new BeatmapSetNominationStatus + { + Current = 4, + RequiredMeta = + { + MainRuleset = 5, + NonMainRuleset = 1, + } + }; + + var multipleRulesets = getManyDifficultiesBeatmapSet(3); + multipleRulesets.HypeStatus = new BeatmapSetHypeStatus(); + multipleRulesets.NominationStatus = new BeatmapSetNominationStatus + { + Current = 4, + RequiredMeta = + { + MainRuleset = 5, + NonMainRuleset = 1, + } + }; + + Child = createContent(OverlayColourScheme.Blue, new Drawable[] + { + new BeatmapCardNormal(singleRuleset), + new BeatmapCardNormal(multipleRulesets), + }); + }); + + // first card: only has main ruleset, required nominations = main_ruleset = 5 + AddAssert("first card has single ruleset", () => firstCard().BeatmapSet.Beatmaps.GroupBy(b => b.Ruleset).Count(), () => Is.EqualTo(1)); + AddAssert("first card nominations = 4/5", () => firstCard().ChildrenOfType().Single().TooltipText.ToString(), () => Is.EqualTo("Nominations: 4/5")); + + // second card: has non-main rulesets, required nominations = main_ruleset + non_main_ruleset * (count of non-main rulesets) = 5 + 1 * 2 = 7 + AddAssert("second card has three rulesets", () => secondCard().BeatmapSet.Beatmaps.GroupBy(b => b.Ruleset).Count(), () => Is.EqualTo(3)); + AddAssert("second card nominations = 4/7", () => secondCard().ChildrenOfType().Single().TooltipText.ToString(), () => Is.EqualTo("Nominations: 4/7")); + + // order is reversed due to the cards being inside a reverse child-id fill flow. + BeatmapCardNormal firstCard() => this.ChildrenOfType().ElementAt(1); + BeatmapCardNormal secondCard() => this.ChildrenOfType().ElementAt(0); + } } }