diff --git a/app/views/user/perfStat.scala b/app/views/user/perfStat.scala
index 763eeff4bddf5..5b512128bc765 100644
--- a/app/views/user/perfStat.scala
+++ b/app/views/user/perfStat.scala
@@ -57,7 +57,7 @@ object perfStat:
div(cls := "box__pad perf-stat__content")(
glicko(user, perfType, user.perfs(perfType), percentile),
counter(stat.count),
- highlow(stat),
+ highlow(stat, percentileLow, percentileHigh),
resultStreak(stat.resultStreak),
result(stat, user),
playStreakNb(stat.playStreak),
@@ -196,20 +196,25 @@ object perfStat:
)
)
- private def highlowSide(title: Frag => Frag, opt: Option[lila.perfStat.RatingAt], color: String)(using
- Lang
- ): Frag = opt match
+ private def highlowSide(
+ title: Frag => Frag,
+ opt: Option[lila.perfStat.RatingAt],
+ pctStr: Option[String],
+ color: String
+ )(using Lang): Frag = opt match
case Some(r) =>
div(
- h2(title(strong(tag(color)(r.int)))),
+ h2(title(strong(tag(color)(r.int, pctStr.map(st.title := _))))),
a(cls := "glpt", href := routes.Round.watcher(r.gameId, "white"))(absClientInstant(r.at))
)
case None => div(h2(title(emptyFrag)), " ", span(notEnoughGames()))
- private def highlow(stat: PerfStat)(using Lang): Frag =
+ private def highlow(stat: PerfStat, pctLow: Option[Double], pctHigh: Option[Double])(using Lang): Frag =
+ import stat.perfType
+ def titleOf(v: Double) = trans.betterThanPercentPlayers.txt(s"$v%", perfType.trans)
st.section(cls := "highlow split")(
- highlowSide(highestRating(_), stat.highest, "green"),
- highlowSide(lowestRating(_), stat.lowest, "red")
+ highlowSide(highestRating(_), stat.highest, pctHigh.map(titleOf), "green"),
+ highlowSide(lowestRating(_), stat.lowest, pctLow.map(titleOf), "red")
)
private def fromTo(s: lila.perfStat.Streak)(using Lang): Frag =
diff --git a/modules/i18n/src/main/I18nKeys.scala b/modules/i18n/src/main/I18nKeys.scala
index af00557fdb37e..331c3425378e7 100644
--- a/modules/i18n/src/main/I18nKeys.scala
+++ b/modules/i18n/src/main/I18nKeys.scala
@@ -557,6 +557,7 @@ object I18nKeys:
val `yourPerfTypeRatingIsRating` = I18nKey("yourPerfTypeRatingIsRating")
val `youAreBetterThanPercentOfPerfTypePlayers` = I18nKey("youAreBetterThanPercentOfPerfTypePlayers")
val `userIsBetterThanPercentOfPerfTypePlayers` = I18nKey("userIsBetterThanPercentOfPerfTypePlayers")
+ val `betterThanPercentPlayers` = I18nKey("betterThanPercentPlayers")
val `youDoNotHaveAnEstablishedPerfTypeRating` = I18nKey("youDoNotHaveAnEstablishedPerfTypeRating")
val `yourRating` = I18nKey("yourRating")
val `cumulative` = I18nKey("cumulative")
diff --git a/modules/perfStat/src/main/PerfStatApi.scala b/modules/perfStat/src/main/PerfStatApi.scala
index 79ef78289127a..e9346e9c3e474 100644
--- a/modules/perfStat/src/main/PerfStatApi.scala
+++ b/modules/perfStat/src/main/PerfStatApi.scala
@@ -8,7 +8,9 @@ case class PerfStatData(
user: User.WithPerfs,
stat: PerfStat,
ranks: UserRankMap,
- percentile: Option[Double]
+ percentile: Option[Double],
+ percentileLow: Option[Double],
+ percentileHigh: Option[Double]
):
def rank = ranks get stat.perfType
@@ -30,16 +32,23 @@ final class PerfStatApi(
!u.isBot || (perfType =!= PerfType.UltraBullet)
.soFu: u =>
for
+
oldPerfStat <- get(u.user, perfType)
perfStat = oldPerfStat.copy(playStreak = oldPerfStat.playStreak.checkCurrent)
+
distribution <- u.perfs(perfType).established soFu rankingApi.weeklyRatingDistribution(perfType)
- percentile = distribution.map: distrib =>
- val (under, sum) = lila.user.Stat.percentile(distrib, u.perfs(perfType).intRating)
- Math.round(under * 1000.0 / sum) / 10.0
- _ = lightUserApi preloadUser u.user
+ percentile = calcPercentile(distribution, u.perfs(perfType).intRating)
+ percentileLow = perfStat.lowest.flatMap { r => calcPercentile(distribution, r.int) }
+ percentileHigh = perfStat.highest.flatMap { r => calcPercentile(distribution, r.int) }
+ _ = lightUserApi preloadUser u.user
_ <- lightUserApi preloadMany perfStat.userIds
- yield PerfStatData(u, perfStat, rankingsOf(u.id), percentile)
+ yield PerfStatData(u, perfStat, rankingsOf(u.id), percentile, percentileLow, percentileHigh)
}
+ private def calcPercentile(wrd: Option[List[Int]], intRating: IntRating): Option[Double] =
+ wrd.map: distrib =>
+ val (under, sum) = lila.user.Stat.percentile(distrib, intRating)
+ Math.round(under * 1000.0 / sum) / 10.0
+
def get(user: User, perfType: PerfType): Fu[PerfStat] =
storage.find(user.id, perfType) getOrElse indexer.userPerf(user, perfType)
diff --git a/translation/source/site.xml b/translation/source/site.xml
index 8d363d28c9dac..7567e29f08de2 100644
--- a/translation/source/site.xml
+++ b/translation/source/site.xml
@@ -707,6 +707,7 @@ computer analysis, game chat and shareable URL.
Your %1$s rating is %2$s.
You are better than %1$s of %2$s players.
%1$s is better than %2$s of %3$s players.
+ Better than %1$s of %2$s players
You do not have an established %s rating.
Your rating
Cumulative