display provisional rating - closes #444

pull/530/head
Thibault Duplessis 2015-06-01 17:27:48 +02:00
parent 3e28a00c76
commit 74630e1ad3
7 changed files with 40 additions and 25 deletions

View File

@ -41,14 +41,16 @@ trait UserHelper { self: I18nHelper with StringHelper =>
best3Of(u, List(PerfType.Bullet, PerfType.Blitz, PerfType.Classical, PerfType.Correspondence)) :::
best3Of(u, List(PerfType.Chess960, PerfType.KingOfTheHill, PerfType.ThreeCheck, PerfType.Antichess, PerfType.Atomic, PerfType.Horde))
def showPerfRating(rating: Int, name: String, nb: Int, icon: Char, klass: String) = Html {
def showPerfRating(rating: Int, name: String, nb: Int, provisional: Boolean, icon: Char, klass: String) = Html {
val title = s"$name rating over $nb games"
val attr = if (klass == "title") "title" else "data-hint"
s"""<span $attr="$title" class="$klass"><span data-icon="$icon">${(nb > 0).fold(rating, "&nbsp;&nbsp;&nbsp;-")}</span></span>"""
val number = if (nb > 0) s"$rating${if (provisional) "?" else ""}"
else "&nbsp;&nbsp;&nbsp;-"
s"""<span $attr="$title" class="$klass"><span data-icon="$icon">$number</span></span>"""
}
def showPerfRating(perfType: PerfType, perf: Perf, klass: String): Html =
showPerfRating(perf.intRating, perfType.name, perf.nb, perfType.iconChar, klass)
showPerfRating(perf.intRating, perfType.name, perf.nb, perf.provisional, perfType.iconChar, klass)
def showPerfRating(u: User, perfType: PerfType, klass: String = "hint--bottom"): Html =
showPerfRating(perfType, u perfs perfType, klass)
@ -226,15 +228,16 @@ trait UserHelper { self: I18nHelper with StringHelper =>
s"""<span $klass $href>$content</span>"""
}
private def renderRating(perf: Perf) =
s"&nbsp;(${perf.intRating}${if (perf.provisional) "?" else ""})"
private def userRating(user: User, withPerfRating: Option[PerfType], withBestRating: Boolean) =
withPerfRating map (_.key) flatMap user.perfs.ratingOf map { rating =>
s"&nbsp;($rating)"
} getOrElse {
withBestRating ?? {
user.perfs.bestPerf ?? {
case (pt, perf) => s"&nbsp;${showPerfRating(pt, perf, "hint--bottom")}"
}
withPerfRating match {
case Some(perfType) => renderRating(user.perfs(perfType))
case _ if withBestRating => user.perfs.bestPerf ?? {
case (_, perf) => renderRating(perf)
}
case _ => ""
}
private def userHref(username: String, params: String = "") =

View File

@ -115,13 +115,15 @@ object GameRepo {
s"${F.whitePlayer}.${Player.BSONFields.ratingDiff}" -> BSONInteger(white._2),
s"${F.blackPlayer}.${Player.BSONFields.ratingDiff}" -> BSONInteger(black._2))))
def setUsers(id: ID, white: Option[(String, Int)], black: Option[(String, Int)]) =
def setUsers(id: ID, white: Option[Player.UserInfo], black: Option[Player.UserInfo]) =
(white.isDefined || black.isDefined) ?? {
$update($select(id), BSONDocument("$set" -> BSONDocument(
s"${F.whitePlayer}.${Player.BSONFields.rating}" -> white.map(_._2).map(BSONInteger.apply),
s"${F.blackPlayer}.${Player.BSONFields.rating}" -> black.map(_._2).map(BSONInteger.apply),
F.playerUids -> lila.db.BSON.writer.listO(List(~white.map(_._1), ~black.map(_._1))),
F.playingUids -> List(white.map(_._1), black.map(_._1)).flatten.distinct
s"${F.whitePlayer}.${Player.BSONFields.rating}" -> white.map(_.rating).map(BSONInteger.apply),
s"${F.blackPlayer}.${Player.BSONFields.rating}" -> black.map(_.rating).map(BSONInteger.apply),
s"${F.whitePlayer}.${Player.BSONFields.provisional}" -> white.map(_.provisional).filter(identity),
s"${F.blackPlayer}.${Player.BSONFields.provisional}" -> black.map(_.provisional).filter(identity),
F.playerUids -> lila.db.BSON.writer.listO(List(~white.map(_.id), ~black.map(_.id))),
F.playingUids -> List(white.map(_.id), black.map(_.id)).flatten.distinct
)))
}

View File

@ -13,12 +13,16 @@ object Namer {
def player(p: Player, withRating: Boolean = true, withTitle: Boolean = true)(implicit lightUser: String => Option[LightUser]) = Html {
p.aiLevel.fold(
p.userId.flatMap(lightUser).fold(lila.user.User.anonymous) { user =>
withRating.fold(
s"${withTitle.fold(user.titleNameHtml, user.name)}&nbsp;(${p.rating getOrElse "?"})",
withTitle.fold(user.titleName, user.name))
if (withRating) s"${withTitle.fold(user.titleNameHtml, user.name)}&nbsp;(${ratingString(p)})"
else withTitle.fold(user.titleName, user.name)
}) { level => s"A.I.&nbsp;level&nbsp;$level" }
}
private def ratingString(p: Player) = p.rating match {
case Some(rating) => s"$rating${if (p.provisional) "?" else ""}"
case _ => "?"
}
def playerString(p: Player, withRating: Boolean = true, withTitle: Boolean = true)(implicit lightUser: String => Option[LightUser]) =
player(p, withRating, withTitle)(lightUser).body.replace("&nbsp;", " ")
}

View File

@ -29,9 +29,8 @@ case class Player(
def withUser(id: String, perf: lila.rating.Perf): Player = copy(
userId = id.some,
rating = perf.intRating.some)
def withRating(rating: Int) = copy(rating = rating.some)
rating = perf.intRating.some,
provisional = perf.glicko.provisional)
def isAi = aiLevel.isDefined
@ -41,7 +40,9 @@ case class Player(
def isUser(u: User) = userId.fold(false)(_ == u.id)
def userInfos: Option[(String, Int)] = (userId |@| rating).tupled
def userInfos: Option[Player.UserInfo] = (userId |@| rating) {
case (id, ra) => Player.UserInfo(id, ra, provisional)
}
def wins = isWinner getOrElse false
@ -104,6 +105,8 @@ object Player {
def suspicious = ply >= 20 && ply <= 30
}
case class UserInfo(id: String, rating: Int, provisional: Boolean)
import reactivemongo.bson.Macros
implicit val holdAlertBSONHandler = Macros.handler[HoldAlert]

View File

@ -36,6 +36,8 @@ case class Perf(
nb)
def nonEmpty = nb > 0
def provisional = glicko.provisional
}
case object Perf {

View File

@ -17,9 +17,10 @@ module.exports = function(ctrl, player, klass) {
href: '/@/' + player.user.username,
target: game.isPlayerPlaying(ctrl.data) ? '_blank' : '_self',
'data-icon': 'r',
title: player.provisional ? 'Provisional rating' : null
}, [
(player.user.title ? player.user.title + ' ' : '') + player.user.username,
rating ? ' (' + rating + ')' : '',
rating ? ' (' + rating + (player.provisional ? '?' : '') + ')' : '',
ratingDiff(player),
player.engine ? m('span[data-icon=j]', {
title: ctrl.trans('thisPlayerUsesChessComputerAssistance')

View File

@ -85,8 +85,8 @@ function renderTablePlay(ctrl) {
return [
renderReplay(ctrl),
m('div.control.icons', [
game.abortable(ctrl.data) ? button.standard(ctrl, null, 'L', 'abortGame', 'abort') :
button.standard(ctrl, game.takebackable, 'i', 'proposeATakeback', 'takeback-yes', partial(ctrl.takebackYes)),
game.abortable(ctrl.data) ? button.standard(ctrl, null, 'L', 'abortGame', 'abort') :
button.standard(ctrl, game.takebackable, 'i', 'proposeATakeback', 'takeback-yes', partial(ctrl.takebackYes)),
button.standard(ctrl, game.drawable, '2', 'offerDraw', 'draw-yes'),
button.standard(ctrl, game.resignable, 'b', 'resign', 'resign')
]),