show user progression, add progression leaderboard, user trophies

This commit is contained in:
Thibault Duplessis 2013-12-21 16:10:14 +01:00
parent d4d8b0e695
commit 00fb927658
18 changed files with 182 additions and 317 deletions

View file

@ -59,14 +59,16 @@ object User extends LilaController {
def list(page: Int) = Open { implicit ctx
Reasonable(page) {
val nb = 15
val nb = 10
env.cached.topProgress(nb) zip
env.cached.topRating(nb) zip
env.cached.topOnline(nb) zip
env.cached.topBullet(nb) zip
env.cached.topBlitz(nb) zip
env.cached.topSlow(nb) zip
env.cached.topNbGame(nb) map {
case (((((rating, online), bullet), blitz), slow), nb) html.user.list(
case ((((((progress, rating), online), bullet), blitz), slow), nb) html.user.list(
progress = progress,
rating = rating,
online = online,
bullet = bullet,

View file

@ -48,7 +48,7 @@ trait StringHelper { self: NumberHelper ⇒
private val delocalize = new lila.common.String.Delocalizer(netDomain)
def showNumber(n: Int): String = (n > 0).fold("+" + n, n.toString)
def showNumber(n: Int): String = if (n > 0) s"+$n" else n.toString
implicit def lilaRichString(str: String) = new {
def active(other: String, one: String = "active") = if (str == other) one else ""
@ -56,7 +56,7 @@ trait StringHelper { self: NumberHelper ⇒
def when(cond: Boolean, str: String) = cond ?? str
def strong(x: Int): String = strong(x.toString)
def strong(x: String): String = "<strong>" + x + "</strong>"
def strong(x: String): String = s"<strong>$x</strong>"
private val NumberFirstRegex = """^(\d+)\s(.+)$""".r
private val NumberLastRegex = """^(.+)\s(\d+)$""".r

View file

@ -1,14 +1,23 @@
package lila.app
package templating
import controllers.routes
import mashup._
import play.api.templates.Html
import controllers.routes
import lila.user.{ User, Context }
import mashup._
trait UserHelper { self: I18nHelper with StringHelper
def showProgress(progress: Int) = Html {
val span = progress match {
case 0 s"""<span class="zero">=</span>"""
case p if p > 0 s"""<span class="positive">$p↗</span>"""
case p if p < 0 s"""<span class="negative">${math.abs(p)}↘</span>"""
}
s"""<span class="progress">$span</span>"""
}
def userIdToUsername(userId: String): String =
(Env.user usernameOrAnonymous userId).await
@ -71,13 +80,14 @@ trait UserHelper { self: I18nHelper with StringHelper ⇒
user: User,
cssClass: Option[String] = None,
withRating: Boolean = true,
withProgress: Boolean = false,
withOnline: Boolean = true,
text: Option[String] = None) = Html {
"""<a %s %s>%s</a>""".format(
userClass(user.id, cssClass, withOnline),
userHref(user.username),
text | withRating.fold(user.usernameWithRating, user.username)
)
val klass = userClass(user.id, cssClass, withOnline)
val href = userHref(user.username)
val content = text | withRating.fold(user.usernameWithRating, user.username)
val progress = withProgress ?? (" " + showProgress(user.progress))
s"""<a $klass $href>$content$progress</a>"""
}
def userInfosLink(

View file

@ -1,4 +1,4 @@
@(rating: List[User], online: List[User], bullet: List[User], blitz: List[User], slow: List[User], nb: List[User])(implicit ctx: Context)
@(progress: List[User], rating: List[User], online: List[User], bullet: List[User], blitz: List[User], slow: List[User], nb: List[User])(implicit ctx: Context)
@goodies = {
<div class="goodies">
@ -15,12 +15,13 @@
@user.layout(trans.chessPlayers.str(), goodies = goodies.some) {
<div class="content_box">
<div class="user_lists clearfix">
@user.top(progress, "Best progress")(_.rating)
@user.top(rating, "Best players ever")(_.rating)
@user.top(online, onlineLink)(_.rating)
@user.top(nb, "Active players", true)(_.count.game.localize + " " + trans.games.str())
@user.top(online, onlineLink, true)(_.rating)
@user.top(nb, "Active players")(u => s"#${u.count.game.localize}")
@user.top(bullet, "Best bullet players")(_.perfs.bullet.glicko.intRating)
@user.top(blitz, "Best blitz players")(_.perfs.blitz.glicko.intRating)
@user.top(slow, "Best slow players", true)(_.perfs.slow.glicko.intRating)
@user.top(blitz, "Best blitz players", true)(_.perfs.blitz.glicko.intRating)
@user.top(slow, "Best slow players")(_.perfs.slow.glicko.intRating)
</div>
</div>
}

View file

@ -28,9 +28,17 @@ robots = false,
evenMoreJs = evenMoreJs) {
<div class="content_box no_padding user_show">
<div class="content_box_top">
@if(info.rank.map(_._1).exists(1==)) {
<img class="best" src="@staticUrl("images/best64.png")" width=64 height=64 title="Best player" />
} else {
@if(info.rank.exists(r => r._1 < r._2 / 100)) {
<img class="trophy" src="@staticUrl("images/trophy64.png")" width=64 height=64 title="Top 1% player" />
}
}
<div class="icon status @{isOnline(u.id).??("connected")}"></div>
<h1 class="lichess_title">@u.username</h1>
<span class="rating">RATING <strong title="Glicko rating">@u.rating</strong><small title="Glicko rating deviation"> ±@u.perfs.global.glicko.intDeviation</small></span>
@showProgress(u.progress)
@info.rank.map { r =>
<span class="rank" title="@trans.rank()">RANK <strong>@r._1.localize</strong> / @r._2.localize</span>
}

View file

@ -5,7 +5,7 @@
<table>
<tbody>
@users.map { u =>
<tr><td>@userLink(u, withRating = false, cssClass="revert-underline".some)</td><td>@f(u)</td></tr>
<tr><td>@userLink(u, withRating = false, withProgress = true, cssClass="revert-underline".some)</td><td>@f(u)</td></tr>
}
</tbody>
</table>

View file

@ -14,7 +14,7 @@ import lila.db.api._
import lila.db.Implicits._
import lila.game.Game
import lila.game.Game.{ BSONFields G }
import lila.round.PerfsUpdater.{ Ratings, resultOf, updateRatings, mkPerfs, system }
import lila.round.PerfsUpdater.{ Ratings, resultOf, updateRatings, mkPerfs, system, makeProgress }
import lila.user.{ User, UserRepo, HistoryRepo, Glicko, GlickoEngine, Perfs, Perf, HistoryEntry }
object GlickoMigration {
@ -30,7 +30,7 @@ object GlickoMigration {
}
val gameColl = lila.game.tube.gameTube.coll
val limit = Int.MaxValue
// val limit = 300000
// val limit = 100000
// val limit = 1000
var nb = 0
@ -121,11 +121,14 @@ object GlickoMigration {
oldUserColl.genericQueryBuilder.cursor[BSONDocument].enumerate() |>>> Iteratee.foreach[BSONDocument] { user
user.getAs[String]("_id") foreach { id
val perfs = userPerfs get id getOrElse Perfs.default
userTube.coll insert {
writeDoc(user, Set("elo", "variantElos", "speedElos")) ++ BSONDocument(
User.BSONFields.perfs -> lila.user.Perfs.tube.handler.write(perfs),
User.BSONFields.rating -> perfs.global.glicko.intRating
)
makeProgress(id) foreach { progress
userTube.coll insert {
writeDoc(user, Set("elo", "variantElos", "speedElos")) ++ BSONDocument(
User.BSONFields.perfs -> lila.user.Perfs.tube.handler.write(perfs),
User.BSONFields.rating -> perfs.global.glicko.intRating,
User.BSONFields.progress -> progress
)
}
}
}
}
@ -145,8 +148,8 @@ object GlickoMigration {
oldUserRepo.engineIds flatMap { engineIds
(enumerator |>>> iteratee(engineIds)) flatMap { _
val perfs = (ratings mapValues mkPerfs).toMap
updateUsers(perfs) flatMap { _
updateHistories(histories) map { _
updateHistories(histories) flatMap { _
updateUsers(perfs) map { _
println("Done!")
"done"
}

View file

@ -41,13 +41,11 @@ object PerfsUpdater {
}
val perfsW = mkPerfs(ratingsW)
val perfsB = mkPerfs(ratingsB)
(UserRepo.setPerfs(white, perfsW) zip
UserRepo.setPerfs(black, perfsB) zip
HistoryRepo.addEntry(white.id, HistoryEntry(
DateTime.now,
perfsW.global.glicko.intRating,
perfsW.global.glicko.intDeviation,
black.perfs.global.glicko.intRating)) zip
(HistoryRepo.addEntry(white.id, HistoryEntry(
DateTime.now,
perfsW.global.glicko.intRating,
perfsW.global.glicko.intDeviation,
black.perfs.global.glicko.intRating)) zip
HistoryRepo.addEntry(black.id, HistoryEntry(
DateTime.now,
perfsB.global.glicko.intRating,
@ -55,7 +53,12 @@ object PerfsUpdater {
white.perfs.global.glicko.intRating)) zip
GameRepo.setRatingDiffs(game.id,
perfsW.global.glicko.intRating - white.perfs.global.glicko.intRating,
perfsB.global.glicko.intRating - black.perfs.global.glicko.intRating))
perfsB.global.glicko.intRating - black.perfs.global.glicko.intRating)) >> {
(makeProgress(white.id) zip makeProgress(black.id)) flatMap {
case (proW, proB)
(UserRepo.setPerfs(white, perfsW, proW) zip UserRepo.setPerfs(black, perfsB, proB))
}
}
}
case _ funit
}) >>
@ -74,6 +77,13 @@ object PerfsUpdater {
val white: Rating,
val black: Rating)
def makeProgress(userId: String): Fu[Int] =
HistoryRepo.userRatings(userId, -10.some) map { entries
~((entries.headOption |@| entries.lastOption) {
case (head, last) last.rating - head.rating
})
}
private def incNbGames(game: Game, user: User): Funit =
UserRepo.incNbGames(user.id, game.rated, game.hasAi,
result = game.nonAi option (game.winnerUserId match {

View file

@ -30,6 +30,7 @@ final class Cached(
timeToLive = ratingChartTtl)
private val topListTtl = 1 minute
val topProgress = AsyncCache(UserRepo.topProgress, timeToLive = topListTtl)
val topRating = AsyncCache(UserRepo.topRating, timeToLive = topListTtl)
val topBullet = AsyncCache(UserRepo.topBullet, timeToLive = topListTtl)
val topBlitz = AsyncCache(UserRepo.topBlitz, timeToLive = topListTtl)

View file

@ -35,28 +35,33 @@ object HistoryRepo {
historyColl.insert(BSONDocument(
"_id" -> user.id,
"e" -> BSONArray(write(HistoryEntry(
DateTime.now,
DateTime.now,
user.perfs.global.glicko.intRating,
user.perfs.global.glicko.intDeviation,
Glicko.default.intRating)))
))
}
def userRatings(userId: String): Fu[List[HistoryEntry]] = {
val arrayReader = implicitly[BSONReader[_ <: BSONValue, BSONArray]].asInstanceOf[BSONReader[BSONValue, BSONArray]]
historyColl.find(BSONDocument("_id" -> userId)).one[BSONDocument] map { historyOption
~(for {
history historyOption
entries history.getAs[BSONArray]("e")
stream = entries.values map arrayReader.readOpt
elems = stream collect {
case Some(array) HistoryEntry(
new DateTime(~array.getAs[Int](0) * 1000l),
~array.getAs[Int](1),
~array.getAs[Int](2),
~array.getAs[Int](3))
}
} yield elems.toList)
}
private val arrayReader = implicitly[BSONReader[_ <: BSONValue, BSONArray]].asInstanceOf[BSONReader[BSONValue, BSONArray]]
def userRatings(userId: String, slice: Option[Int] = None): Fu[List[HistoryEntry]] = {
historyColl.find(
BSONDocument("_id" -> userId),
BSONDocument("_id" -> false) ++ slice.fold(BSONDocument()) { s
BSONDocument("e" -> BSONDocument("$slice" -> s))
}
).one[BSONDocument] map { historyOption
~(for {
history historyOption
entries history.getAs[BSONArray]("e")
stream = entries.values map arrayReader.readOpt
elems = stream collect {
case Some(array) HistoryEntry(
new DateTime(~array.getAs[Int](0) * 1000l),
~array.getAs[Int](1),
~array.getAs[Int](2),
~array.getAs[Int](3))
}
} yield elems.toList)
}
}
}

View file

@ -9,6 +9,7 @@ case class User(
id: String,
username: String,
rating: Int,
progress: Int,
perfs: Perfs,
count: Count,
troll: Boolean = false,
@ -65,6 +66,7 @@ object User {
val id = "_id"
val username = "username"
val rating = "rating"
val progress = "progress"
val perfs = "perfs"
val count = "count"
val troll = "troll"
@ -95,6 +97,7 @@ object User {
id = r str id,
username = r str username,
rating = r nInt rating,
progress = r intD progress,
perfs = r.getO[Perfs](perfs) | Perfs.default,
count = r.get[Count](count),
troll = r boolD troll,
@ -112,6 +115,7 @@ object User {
id -> o.id,
username -> o.username,
rating -> w.int(o.rating),
progress -> w.int(o.progress),
perfs -> o.perfs,
count -> o.count,
troll -> w.boolO(o.troll),

View file

@ -28,6 +28,7 @@ trait UserRepo {
def all: Fu[List[User]] = $find.all
def topRating(nb: Int): Fu[List[User]] = $find(goodLadQuery sort sortRatingDesc, nb)
def topProgress(nb: Int): Fu[List[User]] = $find(stableGoodLadQuery sort sortProgressDesc, nb)
def topBullet = topPerf("bullet") _
def topBlitz = topPerf("blitz") _
@ -66,9 +67,10 @@ trait UserRepo {
def rank(user: User) = $count(enabledSelect ++ Json.obj("rating" -> $gt(Glicko.default.rating))) map (1+)
def setPerfs(user: User, perfs: Perfs) = $update($select(user.id), $setBson(
def setPerfs(user: User, perfs: Perfs, progress: Int) = $update($select(user.id), $setBson(
"perfs" -> Perfs.tube.handler.write(perfs),
"rating" -> BSONInteger(perfs.global.glicko.intRating)
"rating" -> BSONInteger(perfs.global.glicko.intRating),
"progress" -> BSONInteger(progress)
))
def setProfile(id: ID, profile: Profile): Funit =
@ -76,9 +78,14 @@ trait UserRepo {
val enabledSelect = Json.obj("enabled" -> true)
val noEngineSelect = Json.obj("engine" -> $ne(true))
val goodLadQuery = $query(enabledSelect ++ noEngineSelect)
val stableRating = Json.obj("perfs.global.gl.d" -> $lt(100))
val goodLadSelect = enabledSelect ++ noEngineSelect
val stableGoodLadSelect = enabledSelect ++ noEngineSelect ++ goodLadSelect
val goodLadQuery = $query(goodLadSelect)
val stableGoodLadQuery = $query(stableGoodLadSelect)
val sortRatingDesc = $sort desc "rating"
val sortProgressDesc = $sort desc "progress"
def incNbGames(id: ID, rated: Boolean, ai: Boolean, result: Option[Int]) = {
val incs = List(
@ -224,18 +231,20 @@ trait UserRepo {
implicit def countHandler = Count.tube.handler
implicit def perfsHandler = Perfs.tube.handler
import lila.db.BSON.BSONJodaDateTimeHandler
import User.BSONFields
BSONDocument(
"_id" -> normalize(username),
"username" -> username,
BSONFields.id -> normalize(username),
BSONFields.username -> username,
"password" -> hash(password, salt),
"salt" -> salt,
"perfs" -> perfs,
"rating" -> perfs.global.glicko.intRating,
"count" -> Count.default,
"enabled" -> true,
"createdAt" -> DateTime.now,
"seenAt" -> DateTime.now)
BSONFields.perfs -> perfs,
BSONFields.rating -> perfs.global.glicko.intRating,
BSONFields.progress -> 0,
BSONFields.count -> Count.default,
BSONFields.enabled -> true,
BSONFields.createdAt -> DateTime.now,
BSONFields.seenAt -> DateTime.now)
}
private def hash(pass: String, salt: String): String = "%s{%s}".format(pass, salt).sha1

BIN
public/images/best64.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

BIN
public/images/trophy64.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

View file

@ -763,6 +763,15 @@ form.wide input[type="text"], form.wide textarea {
border-top: none;
z-index: 1;
}
span.progress > .zero {
color: #afafaf;
}
span.progress > .positive {
color: #00AA00;
}
span.progress > .negative {
color: #AA0000;
}
div.auth input.username, div.auth input.password {
border: 1px solid #dadada;
padding: 2px 3px;

View file

@ -3,186 +3,48 @@ body.dark {
background-position: left +25px;
color: #909090;
}
body.dark a,
body.dark a:visited {
body.dark a, body.dark a:visited {
color: #909090;
}
body.dark input,
body.dark textarea {
body.dark input, body.dark textarea {
background: #1a1a1a;
color: #909090;
}
/* standard shadow */
body.dark div.lichess_chat,
body.dark div.undertable,
body.dark div.lichess_board_wrap,
body.dark div.lichess_table .lichess_button,
body.dark div.lichess_goodies div.box,
body.dark div.lichess_table,
body.dark div.undergame_box,
body.dark div.clock,
body.dark #GameText,
body.dark #GameBoard table.boardTable,
body.dark .button.strong,
body.dark .button.shadowed,
body.dark #tournament_side,
body.dark #friend_box,
body.dark div.content_box,
body.dark #powerTip
{
body.dark div.lichess_chat, body.dark div.undertable, body.dark div.lichess_board_wrap, body.dark div.lichess_table .lichess_button, body.dark div.lichess_goodies div.box, body.dark div.lichess_table, body.dark div.undergame_box, body.dark div.clock, body.dark #GameText, body.dark #GameBoard table.boardTable, body.dark .button.strong, body.dark .button.shadowed, body.dark #tournament_side, body.dark #friend_box, body.dark div.content_box, body.dark #powerTip {
box-shadow: 0 0 5px #505050;
}
body.dark #top,
body.dark #nb_connected_players,
body.dark #reconnecting,
body.dark #connecting,
body.dark div.lichess_chat_top,
body.dark div.lichess_chat .lichess_messages,
body.dark div.undertable_top,
body.dark div.undertable_inner,
body.dark div.undertable td,
body.dark div.lichess_table .lichess_button,
body.dark div.lichess_chat form input,
body.dark div.lichess_board_wrap,
body.dark #translation_call,
body.dark div.footer_wrap,
body.dark div.game_config .optional_config,
body.dark .ui-state-default,
body.dark .ui-widget-content,
body.dark div.lichess_goodies div.box,
body.dark div.lichess_table,
body.dark div.lichess_separator,
body.dark div.undergame_box,
body.dark div.game_more div.more_top,
body.dark div.clock,
body.dark .button,
body.dark #GameText,
body.dark #tournament_side,
body.dark #GameBoard table.boardTable,
body.dark div.shortcuts .title,
body.dark div.content_box,
body.dark div.content_box_top,
body.dark div.content_box_top a,
body.dark div.content_box_inter,
body.dark div.content_box_inter a.active,
body.dark #site_header div.side_menu a.active,
body.dark div.game_row,
body.dark form.search_user_form input,
body.dark div.user_show div.boxed_data,
body.dark #lichess_forum div.post,
body.dark #lichess_forum textarea,
body.dark #lichess_forum form.wide input,
body.dark #lichess_message tr,
body.dark #lichess_message div.thread_message,
body.dark #lichess_message textarea,
body.dark #lichess_message input,
body.dark .content_box form input,
body.dark div.progressbar,
body.dark form.translation_form div.messages,
body.dark form.translation_form input,
body.dark div.locale_menu a,
body.dark #adv_chart,
body.dark #top .dropdown,
body.dark div.lichess_overboard p.explanations,
body.dark div.search_status,
body.dark table.slist,
body.dark table.slist thead th,
body.dark #notifications > div,
body.dark form.wide input[type="text"],
body.dark form.wide textarea,
body.dark #team .team-left,
body.dark #team .team-right,
body.dark #team h2,
body.dark .leaderboard_title,
body.dark #friend_box,
body.dark #friend_box .title,
body.dark div.user_show div.mod_zone,
body.dark div.user_show div.user-infos,
body.dark div.game_config input[type="text"],
body.dark #powerTip,
body.dark #powerTip > .title,
body.dark #hooks_table th,
body.dark #hooks_table td,
body.dark #hooks_wrap > div.tabs > a,
body.dark #hooks_chart > div.grid
{
body.dark #top, body.dark #nb_connected_players, body.dark #reconnecting, body.dark #connecting, body.dark div.lichess_chat_top, body.dark div.lichess_chat .lichess_messages, body.dark div.undertable_top, body.dark div.undertable_inner, body.dark div.undertable td, body.dark div.lichess_table .lichess_button, body.dark div.lichess_chat form input, body.dark div.lichess_board_wrap, body.dark #translation_call, body.dark div.footer_wrap, body.dark div.game_config .optional_config, body.dark .ui-state-default, body.dark .ui-widget-content, body.dark div.lichess_goodies div.box, body.dark div.lichess_table, body.dark div.lichess_separator, body.dark div.undergame_box, body.dark div.game_more div.more_top, body.dark div.clock, body.dark .button, body.dark #GameText, body.dark #tournament_side, body.dark #GameBoard table.boardTable, body.dark div.shortcuts .title, body.dark div.content_box, body.dark div.content_box_top, body.dark div.content_box_top a, body.dark div.content_box_inter, body.dark div.content_box_inter a.active, body.dark #site_header div.side_menu a.active, body.dark div.game_row, body.dark form.search_user_form input, body.dark div.user_show div.boxed_data, body.dark #lichess_forum div.post, body.dark #lichess_forum textarea, body.dark #lichess_forum form.wide input, body.dark #lichess_message tr, body.dark #lichess_message div.thread_message, body.dark #lichess_message textarea, body.dark #lichess_message input, body.dark .content_box form input, body.dark div.progressbar, body.dark form.translation_form div.messages, body.dark form.translation_form input, body.dark div.locale_menu a, body.dark #adv_chart, body.dark #top .dropdown, body.dark div.lichess_overboard p.explanations, body.dark div.search_status, body.dark table.slist, body.dark table.slist thead th, body.dark #notifications > div, body.dark form.wide input[type="text"], body.dark form.wide textarea, body.dark #team .team-left, body.dark #team .team-right, body.dark #team h2, body.dark .leaderboard_title, body.dark #friend_box, body.dark #friend_box .title, body.dark div.user_show div.mod_zone, body.dark div.user_show div.user-infos, body.dark div.game_config input[type="text"], body.dark #powerTip, body.dark #powerTip > .title, body.dark #hooks_table th, body.dark #hooks_table td, body.dark #hooks_wrap > div.tabs > a, body.dark #hooks_chart > div.grid {
border-color: #444;
}
body.dark #timeline,
body.dark #timeline > .entry {
body.dark #timeline, body.dark #timeline > .entry {
border-color: #303030;
}
body.dark .ui-slider,
body.dark div.progressbar,
body.dark #translation_call div.progressbar,
body.dark #top div.themepicker.shown div.theme:hover {
body.dark .ui-slider, body.dark div.progressbar, body.dark #translation_call div.progressbar, body.dark #top div.themepicker.shown div.theme:hover {
background-color: #505050;
}
body.dark #hooks_wrap > div.tabs > a,
body.dark #top div.auth .links a:hover,
body.dark #top ul.language_links a:hover,
body.dark #top ul.language_links a.current
{
body.dark #hooks_wrap > div.tabs > a, body.dark #top div.auth .links a:hover, body.dark #top ul.language_links a:hover, body.dark #top ul.language_links a.current {
background-color: #3e3e3e;
color: #b0b0b0;
}
body.dark #top ul.language_links a.accepted {
color: #b0b0b0;
}
body.dark #site_title,
body.dark #timeline a,
body.dark #site_baseline,
body.dark div.lichess_chat span,
body.dark div.lichess_chat a.user_link,
body.dark div.lichess_chat a.user_link,
body.dark div.new_posts li span,
body.dark #team .forum a.user_link,
body.dark span.board_mark,
body.dark div.user_show div.content_box_top > span,
body.dark #timeline time
{
body.dark #site_title, body.dark #timeline a, body.dark #site_baseline, body.dark div.lichess_chat span, body.dark div.lichess_chat a.user_link, body.dark div.lichess_chat a.user_link, body.dark div.new_posts li span, body.dark #team .forum a.user_link, body.dark span.board_mark, body.dark div.user_show div.content_box_top > span, body.dark #timeline time, body.dark span.progress > .zero {
color: #777;
}
body.dark #site_title span.extension,
body.dark div.sub_ratings h3
{
body.dark #site_title span.extension, body.dark div.sub_ratings h3 {
color: #606060;
}
body.dark #top a.goto_nav:hover,
body.dark #top a.toggle:hover,
body.dark #top a.bgpicker:hover,
body.dark a#sound_state:hover,
body.dark #top .dropdown,
body.dark #GameText a:hover,
body.dark #hooks_wrap a.filter:hover,
body.dark #hooks_wrap a.filter.active,
body.dark #top .shown a.toggle {
body.dark #top a.goto_nav:hover, body.dark #top a.toggle:hover, body.dark #top a.bgpicker:hover, body.dark a#sound_state:hover, body.dark #top .dropdown, body.dark #GameText a:hover, body.dark #hooks_wrap a.filter:hover, body.dark #hooks_wrap a.filter.active, body.dark #top .shown a.toggle {
background: #000;
}
body.dark #top span.new_messages {
background: #606060;
}
body.dark div.lichess_chat .lichess_messages,
body.dark div.undertable_inner,
body.dark div.lichess_goodies div.box,
body.dark div.undergame_box,
body.dark div.undertable td,
body.dark div.lichess_table,
body.dark div.lichess_table_wrap div.clock,
body.dark #friend_box .content a:hover,
body.dark div.footer_wrap
{
body.dark div.lichess_chat .lichess_messages, body.dark div.undertable_inner, body.dark div.lichess_goodies div.box, body.dark div.undergame_box, body.dark div.undertable td, body.dark div.lichess_table, body.dark div.lichess_table_wrap div.clock, body.dark #friend_box .content a:hover, body.dark div.footer_wrap {
background: #242424;
}
body.dark #hooks_table tr:nth-child(even) td {
background: rgba(70, 70, 70, 0.5);
}
@ -195,9 +57,8 @@ body.dark #hooks_table tr.cancel td {
body.dark #hooks_table tr:hover td {
background: rgba(50, 50, 20, 0.7);
}
body.dark #hook_filter {
background: rgba(0,0,0,0.8);
background: rgba(0, 0, 0, 0.8);
}
body.dark #hook_filter td > label {
color: #c0c0c0;
@ -205,67 +66,34 @@ body.dark #hook_filter td > label {
body.dark #hook_filter td > label.hover:hover {
background: #000;
}
body.dark div.undertable tr:nth-child(even) td,
body.dark #hooks_wrap,
body.dark #hooks_wrap > div.tabs > a:hover,
body.dark #hooks_wrap > div.tabs > a.active {
body.dark div.undertable tr:nth-child(even) td, body.dark #hooks_wrap, body.dark #hooks_wrap > div.tabs > a:hover, body.dark #hooks_wrap > div.tabs > a.active {
background: #303030;
}
body.dark div.lichess_overboard {
background: #2b2b2b;
}
body.dark div.lichess_overboard.game_config {
box-shadow: 0 0 20px #d0d0d0;
}
body.dark div.lichess_table_not_started {
box-shadow: none;
background: none;
}
body.dark div.lichess_table a.lichess_button:hover,
body.dark div.lichess_table a.lichess_button.active {
body.dark div.lichess_table a.lichess_button:hover, body.dark div.lichess_table a.lichess_button.active {
box-shadow: 0 0 9px #d85000;
border: 1px solid #d85000;
}
body.dark div.content_box,
body.dark div.content_box_inter a.active,
body.dark #GameText,
body.dark #tournament_side,
body.dark table.translations tbody tr,
body.dark form.translation_form div.message,
body.dark div.content_box_inter a.intertab:hover
{
body.dark div.content_box, body.dark div.content_box_inter a.active, body.dark #GameText, body.dark #tournament_side, body.dark table.translations tbody tr, body.dark form.translation_form div.message, body.dark div.content_box_inter a.intertab:hover {
background: #2b2b2b;
}
body.dark #site_header div.side_menu a.active {
background: #2b2b2b;
box-shadow: -3px 0 5px #505050;
}
body.dark .mini_board {
box-shadow: none;
}
body.dark div.game_row:nth-child(odd),
body.dark #lichess_forum table.forum_table tr:nth-child(odd),
body.dark #lichess_message tr:nth-child(even),
body.dark div.user_show div.boxed_data,
body.dark div.content_box_inter,
body.dark #GameText tr:nth-child(even),
body.dark table.slist tbody tr:nth-child(even),
body.dark #team .forum li:nth-child(odd),
body.dark table.translations tbody tr:nth-child(odd),
body.dark form.translation_form div.message:nth-child(even),
body.dark div.content_box table.datatable tr:nth-child(odd),
body.dark div.game_config .optional_config,
body.dark div.search_status,
body.dark #friend_box,
body.dark #powerTip
{
body.dark div.game_row:nth-child(odd), body.dark #lichess_forum table.forum_table tr:nth-child(odd), body.dark #lichess_message tr:nth-child(even), body.dark div.user_show div.boxed_data, body.dark div.content_box_inter, body.dark #GameText tr:nth-child(even), body.dark table.slist tbody tr:nth-child(even), body.dark #team .forum li:nth-child(odd), body.dark table.translations tbody tr:nth-child(odd), body.dark form.translation_form div.message:nth-child(even), body.dark div.content_box table.datatable tr:nth-child(odd), body.dark div.game_config .optional_config, body.dark div.search_status, body.dark #friend_box, body.dark #powerTip {
background: #343434;
}
body.dark #lichess_forum table.forum_table thead tr:nth-child(odd) {
@ -274,29 +102,19 @@ body.dark #lichess_forum table.forum_table thead tr:nth-child(odd) {
body.dark #lichess_forum table.forum_table td.subject a {
color: #44a0b0;
}
body.dark #lichess_forum div.post .message,
body.dark #lichess_message div.thread_message div.thread_message_body,
body.dark #GameText .move,
body.dark form.translation_form div.message label,
body.dark div.content_box h1,
body.dark div.user_show div.content_box_top > span strong
{
body.dark #lichess_forum div.post .message, body.dark #lichess_message div.thread_message div.thread_message_body, body.dark #GameText .move, body.dark form.translation_form div.message label, body.dark div.content_box h1, body.dark div.user_show div.content_box_top > span strong {
color: #b0b0b0;
}
body.dark div.lmcs.white {
background-color: #b0b0b0;
}
body.dark div.lmcs.black {
background-color: #8a8a8a;
}
body.dark #hooks_wrap {
background-image: url(../images/knight2000b.png);
}
body.dark div.lichess_cemetery div.lichess_piece,
body.dark div.lichess_table div.lichess_piece {
body.dark div.lichess_cemetery div.lichess_piece, body.dark div.lichess_table div.lichess_piece {
background-image: url(../images/sprite_invert.png);
}
body.dark div.lichess_chat ol.lichess_messages li.white {
@ -305,82 +123,52 @@ body.dark div.lichess_chat ol.lichess_messages li.white {
body.dark div.lichess_chat ol.lichess_messages li.black {
background: url(../images/kdb14.png) no-repeat;
}
body.dark div.lichess_table_wrap div.clock.running {
background: #505a60;
color: #fff;
}
body.dark div.lichess_table_wrap div.clock.running.emerg,
body.dark div.lichess_table_wrap div.clock.outoftime {
body.dark div.lichess_table_wrap div.clock.running.emerg, body.dark div.lichess_table_wrap div.clock.outoftime {
background-color: #a00000;
color: #d0d0d0;
}
/* soft inactive gradient */
body.dark #top,
body.dark div.lichess_chat_top,
body.dark #friend_box .title,
body.dark div.undertable_top,
body.dark .button,
body.dark .button:visited,
body.dark .ui-state-default,
body.dark div.content_box_top,
body.dark #translation_call,
body.dark #notifications > div,
body.dark div.locale_menu a,
body.dark table.slist thead,
body.dark #hooks_wrap > a.no_hook
{
body.dark #top, body.dark div.lichess_chat_top, body.dark #friend_box .title, body.dark div.undertable_top, body.dark .button, body.dark .button:visited, body.dark .ui-state-default, body.dark div.content_box_top, body.dark #translation_call, body.dark #notifications > div, body.dark div.locale_menu a, body.dark table.slist thead, body.dark #hooks_wrap > a.no_hook {
background: #3a3a3a;
color: #b0b0b0;
background: -moz-linear-gradient(center top , #3a3a3a, #202020) repeat scroll 0 0 #3a3a3a;
background: -moz-linear-gradient(center top, #3a3a3a, #202020) repeat scroll 0 0 #3a3a3a;
background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#3a3a3a), to(#202020));
background: -webkit-linear-gradient(top , #3a3a3a, #202020) repeat scroll 0 0 #3a3a3a;
background: -ms-linear-gradient(top , #3a3a3a, #202020) repeat scroll 0 0 #3a3a3a;
background: -o-linear-gradient(top , #3a3a3a, #202020) repeat scroll 0 0 #3a3a3a;
background: -webkit-linear-gradient(top, #3a3a3a, #202020) repeat scroll 0 0 #3a3a3a;
background: -ms-linear-gradient(top, #3a3a3a, #202020) repeat scroll 0 0 #3a3a3a;
background: -o-linear-gradient(top, #3a3a3a, #202020) repeat scroll 0 0 #3a3a3a;
}
/* strong inactive gradient */
body.dark .button.strong,
body.dark .button:hover,
body.dark #hooks_wrap > a.no_hook:hover
{
body.dark .button.strong, body.dark .button:hover, body.dark #hooks_wrap > a.no_hook:hover {
background: #505050;
background: -moz-linear-gradient(center top , #505050, #202020) repeat scroll 0 0 #505050;
background: -moz-linear-gradient(center top, #505050, #202020) repeat scroll 0 0 #505050;
background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#505050), to(#202020));
background: -webkit-linear-gradient(top , #505050, #202020) repeat scroll 0 0 #505050;
background: -ms-linear-gradient(top , #505050, #202020) repeat scroll 0 0 #505050;
background: -o-linear-gradient(top , #505050, #202020) repeat scroll 0 0 #505050;
background: -webkit-linear-gradient(top, #505050, #202020) repeat scroll 0 0 #505050;
background: -ms-linear-gradient(top, #505050, #202020) repeat scroll 0 0 #505050;
background: -o-linear-gradient(top, #505050, #202020) repeat scroll 0 0 #505050;
}
/* active gradient */
body.dark #top a.goto_nav.active,
body.dark .button.active,
body.dark .button.active:hover,
body.dark .ui-state-active,
body.dark .ui-widget-header,
body.dark div.pagination span.current,
body.dark #top span.new_messages,
body.dark .button.strong:hover,
body.dark div.progressbar.flashy div,
body.dark div.locale_menu a.active,
body.dark #top a.signin
{
body.dark #top a.goto_nav.active, body.dark .button.active, body.dark .button.active:hover, body.dark .ui-state-active, body.dark .ui-widget-header, body.dark div.pagination span.current, body.dark #top span.new_messages, body.dark .button.strong:hover, body.dark div.progressbar.flashy div, body.dark div.locale_menu a.active, body.dark #top a.signin {
color: #fff;
background: #d85000;
background: -moz-linear-gradient(center top , #f49c00, #d85000) repeat scroll 0 0 #d85000;
background: -moz-linear-gradient(center top, #f49c00, #d85000) repeat scroll 0 0 #d85000;
background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#f49c00), to(#d85000));
background: -webkit-linear-gradient(top , #f49c00, #d85000) repeat scroll 0 0 #d85000;
background: -ms-linear-gradient(top , #f49c00, #d85000) repeat scroll 0 0 #d85000;
background: -o-linear-gradient(top , #f49c00, #d85000) repeat scroll 0 0 #d85000;
background: -webkit-linear-gradient(top, #f49c00, #d85000) repeat scroll 0 0 #d85000;
background: -ms-linear-gradient(top, #f49c00, #d85000) repeat scroll 0 0 #d85000;
background: -o-linear-gradient(top, #f49c00, #d85000) repeat scroll 0 0 #d85000;
}
/* current gradient */
body.dark #top a.goto_nav.current {
body.dark #top a.goto_nav.current {
color: #fff;
background: #808080;
background: -moz-linear-gradient(center top , #808080, #606060) repeat scroll 0 0 #808080;
background: -moz-linear-gradient(center top, #808080, #606060) repeat scroll 0 0 #808080;
background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#808080), to(#606060));
background: -webkit-linear-gradient(top , #808080, #606060) repeat scroll 0 0 #606060;
background: -ms-linear-gradient(top , #808080, #606060) repeat scroll 0 0 #606060;
background: -o-linear-gradient(top , #808080, #606060) repeat scroll 0 0 #606060;
background: -webkit-linear-gradient(top, #808080, #606060) repeat scroll 0 0 #606060;
background: -ms-linear-gradient(top, #808080, #606060) repeat scroll 0 0 #606060;
background: -o-linear-gradient(top, #808080, #606060) repeat scroll 0 0 #606060;
}
body.dark #hooks_chart .plot {
box-shadow: 0 0 5px #000;
@ -397,7 +185,15 @@ body.dark a#sound_state.sound_state_on span {
body.dark .s16.ddown {
background-position: right -368px;
}
body.dark ::-webkit-input-placeholder { color: #666; }
body.dark :-moz-placeholder { color: #666; }
body.dark ::-moz-placeholder { color: #666; }
body.dark :-ms-input-placeholder { color: #666; }
body.dark ::-webkit-input-placeholder {
color: #666;
}
body.dark :-moz-placeholder {
color: #666;
}
body.dark ::-moz-placeholder {
color: #666;
}
body.dark :-ms-input-placeholder {
color: #666;
}

View file

@ -22,6 +22,12 @@ div.user_show div.content_box_top > span strong {
font-weight: normal;
color: #666;
}
div.user_show div.content_box_top > img.best,
div.user_show div.content_box_top > img.trophy {
float: right;
margin-top: -30px;
margin-right: 10px;
}
div.user_show .social {
position: relative;

1
todo
View file

@ -115,6 +115,7 @@ db.user4.ensureIndex({enabled:1, 'perfs.blitz.gl.r': -1});
db.user4.ensureIndex({enabled:1, 'perfs.slow.gl.r': -1});
db.user4.ensureIndex({enabled:1, 'count.games': -1});
db.user4.ensureIndex({createdAt: -1});
db.user4.ensureIndex({progress:-1,enabled:1,'perfs.global.gl.d':-1})
mongo lichess bin/mongodb/userstats.js
... and reindex all games in elasticsearch D: