many post-deploy fixes
parent
85eafa181a
commit
87846bac10
|
@ -1,13 +1,13 @@
|
|||
package controllers
|
||||
|
||||
import play.api.data._, Forms._
|
||||
import play.api.mvc._, Results._
|
||||
|
||||
import lila.app._
|
||||
import lila.common.LilaCookie
|
||||
import lila.user.{ UserRepo, HistoryRepo }
|
||||
import views._
|
||||
|
||||
import play.api.mvc._, Results._
|
||||
import play.api.data._, Forms._
|
||||
|
||||
object Auth extends LilaController {
|
||||
|
||||
private def api = Env.security.api
|
||||
|
@ -70,6 +70,19 @@ object Auth extends LilaController {
|
|||
)
|
||||
}
|
||||
|
||||
def newPassword = AuthBody { implicit ctx ⇒
|
||||
me ⇒
|
||||
implicit val req = ctx.body
|
||||
forms.newPassword.bindFromRequest.fold(
|
||||
err ⇒ fuccess {
|
||||
BadRequest(html.auth.artificialPassword(me, err))
|
||||
},
|
||||
pass ⇒ UserRepo.artificialSetPassword(me.id, pass) map { _ ⇒
|
||||
Redirect(routes.Lobby.home)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private def gotoLogoutSucceeded(implicit req: RequestHeader) = {
|
||||
req.session get "sessionId" foreach lila.security.Store.delete
|
||||
logoutSucceeded(req) withCookies LilaCookie.newSession
|
||||
|
|
|
@ -23,7 +23,7 @@ object Cli extends LilaController {
|
|||
})
|
||||
}
|
||||
|
||||
private def CliAuth(password: String)(op: Fu[SimpleResult]): Fu[SimpleResult] =
|
||||
private def CliAuth(password: String)(op: => Fu[SimpleResult]): Fu[SimpleResult] =
|
||||
lila.user.UserRepo.checkPassword(Env.api.CliUsername, password) flatMap {
|
||||
_.fold(op, fuccess(Unauthorized))
|
||||
}
|
||||
|
|
|
@ -13,9 +13,14 @@ import views._
|
|||
object Lobby extends LilaController {
|
||||
|
||||
def home = Open { implicit ctx ⇒
|
||||
renderHome(Results.Ok).map(_.withHeaders(
|
||||
CACHE_CONTROL -> "no-cache", PRAGMA -> "no-cache"
|
||||
))
|
||||
ctx.me match {
|
||||
case Some(u) if u.artificial ⇒ fuccess {
|
||||
views.html.auth.artificialPassword(u, Env.security.forms.newPassword)
|
||||
}
|
||||
case _ ⇒ renderHome(Results.Ok).map(_.withHeaders(
|
||||
CACHE_CONTROL -> "no-cache", PRAGMA -> "no-cache"
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
def handleStatus(req: RequestHeader, status: Results.Status): Fu[SimpleResult] =
|
||||
|
|
|
@ -39,7 +39,9 @@ object User extends LilaController {
|
|||
OptionFuResult(UserRepo named username) { u ⇒
|
||||
(u.enabled || isGranted(_.UserSpy)).fold({
|
||||
userShow(u, filterName, page) map { Ok(_) }
|
||||
}, fuccess(NotFound(html.user.disabled(u))))
|
||||
}, UserRepo isArtificial u.id map { artificial ⇒
|
||||
NotFound(html.user.disabled(u, artificial))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
@(user: User, form: Form[_])(implicit ctx: Context)
|
||||
|
||||
@auth.layout("Please enter your password") {
|
||||
<div class="content_box" style="width: 400px;">
|
||||
<h1 class="lichess_title">Please enter your password</h1>
|
||||
<p>
|
||||
Due to a system failure that happened the 22 Dec. 2013,<br />
|
||||
You have to re-enter your password to continue using lichess.<br />
|
||||
For details, see <a href="http://en.lichess.org/forum/lichess-feedback/server-failure--data-has-been-lost">The announcement</a>.
|
||||
</p>
|
||||
<form action="@routes.Auth.newPassword" method="POST">
|
||||
@form.globalError.map { error =>
|
||||
<p class="error">@error.message</p>
|
||||
}
|
||||
<ul>
|
||||
<li class="password">
|
||||
<label for="@form("password").id">@trans.password()</label>
|
||||
<input
|
||||
type="password"
|
||||
required="required"
|
||||
name="@form("password").name"
|
||||
id="@form("password").id" />
|
||||
@errMsg(form("password"))
|
||||
</li>
|
||||
<li>
|
||||
<input type="submit" value="@trans.apply()" class="submit button">
|
||||
</li>
|
||||
</ul>
|
||||
</form>
|
||||
</div>
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
@(u: User)(implicit ctx: Context)
|
||||
@(u: User, artificial: Boolean)(implicit ctx: Context)
|
||||
|
||||
@user.layout(
|
||||
title = u.username,
|
||||
|
@ -9,6 +9,11 @@ robots = false) {
|
|||
</div>
|
||||
<div class="content_box_content clearfix">
|
||||
This account is closed.
|
||||
@if(artificial) {
|
||||
This account infos have been lost during a server failure.<br />
|
||||
All new accounts between 12 Dec 2013 and 22 Dec 2013 are affected.<br />
|
||||
We are very sorry for the inconvenience.
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
|
|
@ -7,10 +7,20 @@
|
|||
</form>
|
||||
|
||||
<div class="user_lists">
|
||||
@user.top(online, "Online players") { u =>
|
||||
<td>@u.rating</td>
|
||||
<td>@showProgress(u.progress)</td>
|
||||
}
|
||||
<div class="user_top">
|
||||
<h2>Online players</h2>
|
||||
<table>
|
||||
<tbody>
|
||||
@online.map { u =>
|
||||
<tr>
|
||||
<td>@userLink(u, withOnline = false, withRating = false, cssClass="revert-underline".some)</td>
|
||||
<td>@u.rating</td>
|
||||
<td>@showProgress(u.progress)</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<a class="more" href="@routes.User.online" title="@trans.more()">@trans.more() »</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
. bin/lilarc
|
||||
|
||||
|
||||
mkdir -p public/compiled
|
||||
mkdir -p public/javascripts/compiled
|
||||
for file in big.js chart2.js user.js boardEditor.js pgn4hacks.js; do
|
||||
orig=public/javascripts/$file
|
||||
comp=public/compiled/$file
|
||||
comp=public/javascripts/compiled/$file
|
||||
if [[ ! -f $comp || $orig -nt $comp ]]; then
|
||||
lilalog "Compiling lila javascript - $file"
|
||||
closure --js $orig --js_output_file $comp
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
db.tournament.find({}, {players:1}).forEach(function(tour) {
|
||||
for (var i in tour.players) {
|
||||
var p = tour.players[i];
|
||||
p.rating = p.elo;
|
||||
delete p.elo;
|
||||
tour.players[i] = p;
|
||||
}
|
||||
db.tournament.update({"_id": tour._id}, {"$set":{players: tour.players}});
|
||||
});
|
|
@ -1,5 +1,5 @@
|
|||
var games = db.game5;
|
||||
var users = db.user3;
|
||||
var users = db.user4;
|
||||
|
||||
var batchSize = 1000;
|
||||
var i, t, timeStrings, times, it=0;
|
||||
|
|
|
@ -140,6 +140,7 @@ POST /login controllers.Auth.authenticate
|
|||
GET /logout controllers.Auth.logout
|
||||
GET /signup controllers.Auth.signup
|
||||
POST /signup controllers.Auth.signupPost
|
||||
POST /new-password controllers.Auth.newPassword
|
||||
|
||||
# Mod
|
||||
POST /mod/:username/engine controllers.Mod.engine(username: String)
|
||||
|
|
|
@ -18,7 +18,7 @@ final class Env(
|
|||
val IsServer = config getBoolean "server"
|
||||
val IsClient = config getBoolean "client"
|
||||
val StockfishRemotes = config getStringList "stockfish.remotes" toList
|
||||
val StockfishLocal = config getString "stockfish.local"
|
||||
val StockfishLocal = config getString "stockfish.local"
|
||||
val StockfishPlayRoute = config getString "stockfish.play.route"
|
||||
val StockfishAnalyseRoute = config getString "stockfish.analyse.route"
|
||||
val StockfishLoadRoute = config getString "stockfish.load.route"
|
||||
|
@ -69,7 +69,7 @@ final class Env(
|
|||
playRoute = StockfishPlayRoute,
|
||||
analyseRoute = StockfishAnalyseRoute,
|
||||
loadRoute = StockfishLoadRoute) _
|
||||
)
|
||||
)
|
||||
), name = "stockfish-dispatcher"),
|
||||
fallback = stockfishServer,
|
||||
config = stockfishConfig,
|
||||
|
|
|
@ -22,6 +22,14 @@ private[api] final class Cli(bus: lila.common.Bus, renderer: ActorSelection) ext
|
|||
lila.db.Env.current,
|
||||
lila.game.Env.current,
|
||||
lila.user.Env.current)
|
||||
case "glicko" :: "migration" :: "end" :: Nil => GlickoMigrationEnd(
|
||||
lila.db.Env.current,
|
||||
lila.game.Env.current,
|
||||
lila.user.Env.current)
|
||||
case "glicko" :: "migration" :: "fix" :: Nil => GlickoMigrationFix(
|
||||
lila.db.Env.current,
|
||||
lila.game.Env.current,
|
||||
lila.user.Env.current)
|
||||
}
|
||||
|
||||
private def remindDeploy(event: RemindDeploy): Fu[String] = {
|
||||
|
|
|
@ -0,0 +1,172 @@
|
|||
package lila.api
|
||||
|
||||
import scala.concurrent.duration._
|
||||
import scala.concurrent.Future
|
||||
import scala.util.{ Try, Success, Failure }
|
||||
|
||||
import org.goochjs.glicko2._
|
||||
import org.joda.time.DateTime
|
||||
import play.api.libs.iteratee._
|
||||
import play.api.libs.json.Json
|
||||
import reactivemongo.bson._
|
||||
|
||||
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, mkPerf, system, makeProgress }
|
||||
import lila.user.{ User, UserRepo, HistoryRepo, Glicko, GlickoEngine, Perfs, Perf, HistoryEntry }
|
||||
|
||||
object GlickoMigrationEnd {
|
||||
|
||||
def apply(
|
||||
db: lila.db.Env,
|
||||
gameEnv: lila.game.Env,
|
||||
userEnv: lila.user.Env) = {
|
||||
|
||||
val oldUserColl = db("user3")
|
||||
val oldUserRepo = new UserRepo {
|
||||
def userTube = lila.user.tube.userTube inColl oldUserColl
|
||||
}
|
||||
val gameColl = lila.game.tube.gameTube.coll
|
||||
val limit = Int.MaxValue
|
||||
// val limit = 100000
|
||||
// val limit = 1000
|
||||
var nb = 0
|
||||
|
||||
import scala.collection.mutable
|
||||
val ratings = mutable.Map.empty[String, Ratings]
|
||||
val histories = mutable.Map.empty[String, mutable.ListBuffer[HistoryEntry]]
|
||||
|
||||
val enumerator: Enumerator[Option[Game]] = lila.game.tube.gameTube |> { implicit gameTube ⇒
|
||||
import Game.gameBSONHandler
|
||||
$query(lila.game.Query.rated)
|
||||
// .batch(1000)
|
||||
.sort($sort asc G.createdAt)
|
||||
.cursor[Option[Game]].enumerate(limit, false)
|
||||
}
|
||||
|
||||
def iteratee(isEngine: Set[String]): Iteratee[Option[Game], Unit] = {
|
||||
Iteratee.foreach[Option[Game]] {
|
||||
_ foreach { game ⇒
|
||||
nb = nb + 1
|
||||
if (nb % 1000 == 0) println(nb)
|
||||
game.userIds match {
|
||||
// case _ if game.source.exists(lila.game.Source.Position ==) ⇒ unrate(game)
|
||||
// case List(uidW, uidB) if (uidW == uidB) ⇒ unrate(game)
|
||||
// case List(uidW, uidB) if isEngine(uidW) || isEngine(uidB) ⇒ unrate(game)
|
||||
case List(uidW, uidB) ⇒ {
|
||||
val ratingsW = ratings.getOrElseUpdate(uidW, mkRatings)
|
||||
val ratingsB = ratings.getOrElseUpdate(uidB, mkRatings)
|
||||
val prevRatingW = ratingsW.global.getRating.toInt
|
||||
val prevRatingB = ratingsB.global.getRating.toInt
|
||||
val result = resultOf(game)
|
||||
updateRatings(ratingsW.global, ratingsB.global, result, system)
|
||||
updateRatings(ratingsW.white, ratingsB.black, result, system)
|
||||
game.variant match {
|
||||
case chess.Variant.Standard ⇒
|
||||
updateRatings(ratingsW.standard, ratingsB.standard, result, system)
|
||||
case chess.Variant.Chess960 ⇒
|
||||
updateRatings(ratingsW.chess960, ratingsB.chess960, result, system)
|
||||
case _ ⇒
|
||||
}
|
||||
chess.Speed(game.clock) match {
|
||||
case chess.Speed.Bullet ⇒
|
||||
updateRatings(ratingsW.bullet, ratingsB.bullet, result, system)
|
||||
case chess.Speed.Blitz ⇒
|
||||
updateRatings(ratingsW.blitz, ratingsB.blitz, result, system)
|
||||
case chess.Speed.Slow | chess.Speed.Unlimited ⇒
|
||||
updateRatings(ratingsW.slow, ratingsB.slow, result, system)
|
||||
}
|
||||
// histories.getOrElseUpdate(uidW, mkHistory) +=
|
||||
// HistoryEntry(game.createdAt, ratingsW.global.getRating.toInt, ratingsW.global.getRatingDeviation.toInt, prevRatingB)
|
||||
// histories.getOrElseUpdate(uidB, mkHistory) +=
|
||||
// HistoryEntry(game.createdAt, ratingsB.global.getRating.toInt, ratingsB.global.getRatingDeviation.toInt, prevRatingW)
|
||||
// gameColl.uncheckedUpdate(
|
||||
// BSONDocument("_id" -> game.id),
|
||||
// BSONDocument("$set" -> BSONDocument(
|
||||
// "p0.e" -> prevRatingW,
|
||||
// "p0.d" -> (ratingsW.global.getRating.toInt - prevRatingW),
|
||||
// "p1.e" -> prevRatingB,
|
||||
// "p1.d" -> (ratingsB.global.getRating.toInt - prevRatingB)
|
||||
// )))
|
||||
}
|
||||
case _ ⇒
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// def unrate(game: Game) {
|
||||
// gameColl.uncheckedUpdate(
|
||||
// BSONDocument("_id" -> game.id),
|
||||
// BSONDocument("$unset" -> BSONDocument(
|
||||
// "ra" -> true,
|
||||
// "p0.d" -> true,
|
||||
// "p1.d" -> true
|
||||
// )))
|
||||
// }
|
||||
|
||||
// def mkHistory = mutable.ListBuffer(
|
||||
// HistoryEntry(DateTime.now, Glicko.default.intRating, Glicko.default.intDeviation, Glicko.default.intRating)
|
||||
// )
|
||||
|
||||
def mkRatings = {
|
||||
def r = new Rating(system.getDefaultRating, system.getDefaultRatingDeviation, system.getDefaultVolatility, 0)
|
||||
new Ratings(r, r, r, r, r, r, r, r)
|
||||
}
|
||||
|
||||
def updateUsers(userPerfs: Map[String, Perfs]): Future[Unit] = lila.user.tube.userTube |> { implicit userTube ⇒
|
||||
userTube.coll.drop() flatMap { _ ⇒
|
||||
oldUserColl.genericQueryBuilder.cursor[BSONDocument].enumerate() |>>> Iteratee.foreach[BSONDocument] { user ⇒
|
||||
user.getAs[String]("_id") foreach { id ⇒
|
||||
val perfs = userPerfs get id getOrElse Perfs.default
|
||||
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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// def updateHistories(histories: Iterable[(String, Iterable[HistoryEntry])]): Funit = {
|
||||
// userEnv.historyColl.drop() recover {
|
||||
// case e: Exception ⇒ fuccess()
|
||||
// } flatMap { _ ⇒
|
||||
// Future.traverse(histories) {
|
||||
// case (id, history) ⇒ HistoryRepo.set(id, history)
|
||||
// }
|
||||
// }
|
||||
// }.void
|
||||
|
||||
def mkPerfs(ratings: Ratings): Perfs = Perfs(
|
||||
global = mkPerf(ratings.global, None),
|
||||
standard = mkPerf(ratings.standard, None),
|
||||
chess960 = mkPerf(ratings.chess960, None),
|
||||
bullet = mkPerf(ratings.bullet, None),
|
||||
blitz = mkPerf(ratings.blitz, None),
|
||||
slow = mkPerf(ratings.slow, None),
|
||||
white = mkPerf(ratings.white, None),
|
||||
black = mkPerf(ratings.black, None))
|
||||
|
||||
oldUserRepo.engineIds flatMap { engineIds ⇒
|
||||
(enumerator |>>> iteratee(engineIds)) flatMap { _ ⇒
|
||||
val perfs = (ratings mapValues mkPerfs).toMap
|
||||
updateUsers(perfs) map { _ ⇒
|
||||
println("Done!")
|
||||
"done"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def writeDoc(doc: BSONDocument, drops: Set[String]) = BSONDocument(doc.elements collect {
|
||||
case (k, v) if !drops(k) ⇒ k -> v
|
||||
})
|
||||
}
|
|
@ -0,0 +1,205 @@
|
|||
package lila.api
|
||||
|
||||
import scala.concurrent.duration._
|
||||
import scala.concurrent.Future
|
||||
import scala.util.{ Try, Success, Failure }
|
||||
|
||||
import org.goochjs.glicko2._
|
||||
import org.joda.time.DateTime
|
||||
import play.api.libs.iteratee._
|
||||
import play.api.libs.json.Json
|
||||
import reactivemongo.bson._
|
||||
|
||||
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, mkPerf, system, makeProgress }
|
||||
import lila.user.{ User, UserRepo, HistoryRepo, Glicko, GlickoEngine, Perfs, Perf, HistoryEntry }
|
||||
|
||||
object GlickoMigrationFix {
|
||||
|
||||
def apply(
|
||||
db: lila.db.Env,
|
||||
gameEnv: lila.game.Env,
|
||||
userEnv: lila.user.Env) = {
|
||||
|
||||
val oldUserColl = db("user3")
|
||||
val oldUserRepo = new UserRepo {
|
||||
def userTube = lila.user.tube.userTube inColl oldUserColl
|
||||
}
|
||||
val gameColl = lila.game.tube.gameTube.coll
|
||||
val limit = Int.MaxValue
|
||||
// val limit = 100000
|
||||
// val limit = 1000
|
||||
var nb = 0
|
||||
|
||||
import scala.collection.mutable
|
||||
val ratings = mutable.Map.empty[String, Ratings]
|
||||
val histories = mutable.Map.empty[String, mutable.ListBuffer[HistoryEntry]]
|
||||
|
||||
val enumerator: Enumerator[Option[Game]] = lila.game.tube.gameTube |> { implicit gameTube ⇒
|
||||
import Game.gameBSONHandler
|
||||
$query(lila.game.Query.rated)
|
||||
// .batch(1000)
|
||||
.sort($sort asc G.createdAt)
|
||||
.cursor[Option[Game]].enumerate(limit, false)
|
||||
}
|
||||
|
||||
def iteratee(isEngine: Set[String]): Iteratee[Option[Game], Unit] = {
|
||||
Iteratee.foreach[Option[Game]] {
|
||||
_ foreach { game ⇒
|
||||
nb = nb + 1
|
||||
if (nb % 1000 == 0) println(nb)
|
||||
game.userIds match {
|
||||
// case _ if game.source.exists(lila.game.Source.Position ==) ⇒ unrate(game)
|
||||
// case List(uidW, uidB) if (uidW == uidB) ⇒ unrate(game)
|
||||
// case List(uidW, uidB) if isEngine(uidW) || isEngine(uidB) ⇒ unrate(game)
|
||||
case List(uidW, uidB) ⇒ {
|
||||
val ratingsW = ratings.getOrElseUpdate(uidW, mkRatings)
|
||||
val ratingsB = ratings.getOrElseUpdate(uidB, mkRatings)
|
||||
val prevRatingW = ratingsW.global.getRating.toInt
|
||||
val prevRatingB = ratingsB.global.getRating.toInt
|
||||
val result = resultOf(game)
|
||||
updateRatings(ratingsW.global, ratingsB.global, result, system)
|
||||
updateRatings(ratingsW.white, ratingsB.black, result, system)
|
||||
game.variant match {
|
||||
case chess.Variant.Standard ⇒
|
||||
updateRatings(ratingsW.standard, ratingsB.standard, result, system)
|
||||
case chess.Variant.Chess960 ⇒
|
||||
updateRatings(ratingsW.chess960, ratingsB.chess960, result, system)
|
||||
case _ ⇒
|
||||
}
|
||||
chess.Speed(game.clock) match {
|
||||
case chess.Speed.Bullet ⇒
|
||||
updateRatings(ratingsW.bullet, ratingsB.bullet, result, system)
|
||||
case chess.Speed.Blitz ⇒
|
||||
updateRatings(ratingsW.blitz, ratingsB.blitz, result, system)
|
||||
case chess.Speed.Slow | chess.Speed.Unlimited ⇒
|
||||
updateRatings(ratingsW.slow, ratingsB.slow, result, system)
|
||||
}
|
||||
// histories.getOrElseUpdate(uidW, mkHistory) +=
|
||||
// HistoryEntry(game.createdAt, ratingsW.global.getRating.toInt, ratingsW.global.getRatingDeviation.toInt, prevRatingB)
|
||||
// histories.getOrElseUpdate(uidB, mkHistory) +=
|
||||
// HistoryEntry(game.createdAt, ratingsB.global.getRating.toInt, ratingsB.global.getRatingDeviation.toInt, prevRatingW)
|
||||
// gameColl.uncheckedUpdate(
|
||||
// BSONDocument("_id" -> game.id),
|
||||
// BSONDocument("$set" -> BSONDocument(
|
||||
// "p0.e" -> prevRatingW,
|
||||
// "p0.d" -> (ratingsW.global.getRating.toInt - prevRatingW),
|
||||
// "p1.e" -> prevRatingB,
|
||||
// "p1.d" -> (ratingsB.global.getRating.toInt - prevRatingB)
|
||||
// )))
|
||||
}
|
||||
case _ ⇒
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def unrate(game: Game) {
|
||||
gameColl.uncheckedUpdate(
|
||||
BSONDocument("_id" -> game.id),
|
||||
BSONDocument("$unset" -> BSONDocument(
|
||||
"ra" -> true,
|
||||
"p0.d" -> true,
|
||||
"p1.d" -> true
|
||||
)))
|
||||
}
|
||||
|
||||
def mkHistory = mutable.ListBuffer(
|
||||
HistoryEntry(DateTime.now, Glicko.default.intRating, Glicko.default.intDeviation, Glicko.default.intRating)
|
||||
)
|
||||
|
||||
def mkRatings = {
|
||||
def r = new Rating(system.getDefaultRating, system.getDefaultRatingDeviation, system.getDefaultVolatility, 0)
|
||||
new Ratings(r, r, r, r, r, r, r, r)
|
||||
}
|
||||
|
||||
def updateUsers(userPerfs: Map[String, Perfs]): Future[Unit] = lila.user.tube.userTube |> { implicit userTube ⇒
|
||||
userTube.coll.drop() flatMap { _ ⇒
|
||||
oldUserColl.genericQueryBuilder.cursor[BSONDocument].enumerate() |>>> Iteratee.foreach[BSONDocument] { user ⇒
|
||||
user.getAs[String]("_id") foreach { id ⇒
|
||||
val perfs = userPerfs get id getOrElse Perfs.default
|
||||
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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val createdAt = new DateTime(2013, 12, 12, 0, 0)
|
||||
def createArtificialUsers(userPerfs: Map[String, Perfs]): Future[Unit] = lila.user.tube.userTube |> { implicit userTube ⇒
|
||||
Future.traverse(userPerfs) {
|
||||
case (id, perfs) => UserRepo idExists id flatMap {
|
||||
case false if id.size <= 20 => {
|
||||
println(s"Create artificial user $id")
|
||||
makeProgress(id) flatMap { progress ⇒
|
||||
UserRepo.insertArtificialUser(id, perfs, progress, createdAt)
|
||||
}
|
||||
}
|
||||
case _ => funit
|
||||
}
|
||||
} void
|
||||
}
|
||||
|
||||
// def disown(game: Game) {
|
||||
// gameColl.uncheckedUpdate(
|
||||
// BSONDocument("_id" -> game.id),
|
||||
// BSONDocument("$unset" -> BSONDocument(
|
||||
// "ra" -> true,
|
||||
// "p0.d" -> true,
|
||||
// "p0.e" -> true,
|
||||
// "p1.d" -> true,
|
||||
// "p1.e" -> true,
|
||||
// "us" -> true,
|
||||
// "wid" -> true
|
||||
// )))
|
||||
// }
|
||||
|
||||
def updateHistories(histories: Iterable[(String, Iterable[HistoryEntry])]): Funit = {
|
||||
userEnv.historyColl.drop() recover {
|
||||
case e: Exception ⇒ fuccess()
|
||||
} flatMap { _ ⇒
|
||||
Future.traverse(histories) {
|
||||
case (id, history) ⇒ HistoryRepo.set(id, history)
|
||||
}
|
||||
}
|
||||
}.void
|
||||
|
||||
def mkPerfs(ratings: Ratings): Perfs = Perfs(
|
||||
global = mkPerf(ratings.global, None),
|
||||
standard = mkPerf(ratings.standard, None),
|
||||
chess960 = mkPerf(ratings.chess960, None),
|
||||
bullet = mkPerf(ratings.bullet, None),
|
||||
blitz = mkPerf(ratings.blitz, None),
|
||||
slow = mkPerf(ratings.slow, None),
|
||||
white = mkPerf(ratings.white, None),
|
||||
black = mkPerf(ratings.black, None))
|
||||
|
||||
oldUserRepo.engineIds flatMap { engineIds ⇒
|
||||
(enumerator |>>> iteratee(engineIds)) flatMap { _ ⇒
|
||||
val perfs = (ratings mapValues mkPerfs).toMap
|
||||
// updateHistories(histories) flatMap { _ ⇒
|
||||
updateUsers(perfs) flatMap { _ ⇒
|
||||
createArtificialUsers(perfs) map { _ =>
|
||||
println("Done!")
|
||||
"done"
|
||||
}
|
||||
}
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def writeDoc(doc: BSONDocument, drops: Set[String]) = BSONDocument(doc.elements collect {
|
||||
case (k, v) if !drops(k) ⇒ k -> v
|
||||
})
|
||||
}
|
|
@ -230,7 +230,7 @@ trait GameRepo {
|
|||
_.headOption flatMap extractPgnMoves
|
||||
} map (~_)
|
||||
|
||||
def activePlayersSince(since: DateTime)(nb: Int): Fu[List[(String, Int)]] = {
|
||||
def activePlayersSince(since: DateTime)(max: Int): Fu[List[(String, Int)]] = {
|
||||
import reactivemongo.bson._
|
||||
import reactivemongo.core.commands._
|
||||
import lila.db.BSON.BSONJodaDateTimeHandler
|
||||
|
@ -245,7 +245,8 @@ trait GameRepo {
|
|||
BSONFields.playerUids -> BSONDocument("$ne" -> "")
|
||||
)),
|
||||
GroupField(Game.BSONFields.winnerId)("nb" -> SumValue(1)),
|
||||
Sort(Seq(Descending("nb")))
|
||||
Sort(Seq(Descending("nb"))),
|
||||
Limit(max)
|
||||
))
|
||||
gameTube.coll.db.command(command) map { stream ⇒
|
||||
(stream.toList map { obj ⇒
|
||||
|
|
|
@ -29,6 +29,10 @@ final class DataForm(val captcher: akka.actor.ActorSelection) extends lila.hub.C
|
|||
|
||||
def signupWithCaptcha = withCaptcha(signup)
|
||||
|
||||
val newPassword = Form(single(
|
||||
"password" -> text(minLength = 4)
|
||||
))
|
||||
|
||||
private def userExists(data: SignupData) =
|
||||
$count.exists(data.username.toLowerCase)
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ private[setup] final class FormFactory {
|
|||
"variant" -> list(variant),
|
||||
"mode" -> list(rawMode(true)),
|
||||
"speed" -> list(speed),
|
||||
"eloRange" -> eloRange
|
||||
"ratingRange" -> ratingRange
|
||||
)(FilterConfig.<<)(_.>>)
|
||||
)
|
||||
|
||||
|
@ -83,7 +83,7 @@ private[setup] final class FormFactory {
|
|||
"increment" -> increment,
|
||||
"mode" -> mode(ctx.isAuth),
|
||||
"membersOnly" -> boolean,
|
||||
"eloRange" -> optional(eloRange),
|
||||
"ratingRange" -> optional(ratingRange),
|
||||
"color" -> nonEmptyText.verifying(Color.names contains _)
|
||||
)(HookConfig.<<)(_.>>)
|
||||
.verifying("Invalid clock", _.validClock)
|
||||
|
|
|
@ -17,7 +17,7 @@ object Mappings {
|
|||
def rawMode(isAuth: Boolean) = number
|
||||
.verifying(HookConfig.modes contains _)
|
||||
.verifying(m ⇒ m == Mode.Casual.id || isAuth)
|
||||
val eloRange = nonEmptyText.verifying(RatingRange valid _)
|
||||
val ratingRange = nonEmptyText.verifying(RatingRange valid _)
|
||||
val color = nonEmptyText.verifying(Color.names contains _)
|
||||
val level = number.verifying(AiConfig.levels contains _)
|
||||
val speed = number.verifying(Config.speeds contains _)
|
||||
|
|
|
@ -32,8 +32,8 @@ private[tournament] object Player {
|
|||
.filter(p ⇒ p.finished && (p contains player.id))
|
||||
.foldLeft(Builder(player))(_ + _.winner)
|
||||
.toPlayer
|
||||
} sortBy { p ⇒
|
||||
p.withdraw.fold(Int.MaxValue, 0) - p.score
|
||||
} sortBy { p ⇒
|
||||
p.withdraw.fold(Int.MaxValue, 0) - p.score
|
||||
}
|
||||
|
||||
private case class Builder(
|
||||
|
@ -46,7 +46,7 @@ private[tournament] object Player {
|
|||
prevWin: Boolean = false) {
|
||||
|
||||
def +(winner: Option[String]) = {
|
||||
val (win, loss): Pair[Boolean, Boolean] = winner.fold(false -> false) { w =>
|
||||
val (win, loss): Pair[Boolean, Boolean] = winner.fold(false -> false) { w ⇒
|
||||
if (w == player.id) true -> false else false -> true
|
||||
}
|
||||
val newWinSeq = if (win) prevWin.fold(winSeq + 1, 1) else 0
|
||||
|
|
|
@ -2,13 +2,13 @@ package lila.tournament
|
|||
|
||||
import akka.actor.{ ActorRef, ActorSelection }
|
||||
import akka.pattern.{ ask, pipe }
|
||||
import org.joda.time.DateTime
|
||||
import chess.{ Mode, Variant }
|
||||
import com.github.nscala_time.time.Imports._
|
||||
import org.joda.time.DateTime
|
||||
import play.api.libs.json._
|
||||
import scalaz.NonEmptyList
|
||||
|
||||
import actorApi._
|
||||
import chess.{ Mode, Variant }
|
||||
import lila.db.api._
|
||||
import lila.game.{ Game, GameRepo }
|
||||
import lila.hub.actorApi.lobby.ReloadTournaments
|
||||
|
@ -73,7 +73,7 @@ private[tournament] final class TournamentApi(
|
|||
$remove(created) >>
|
||||
$remove.byId[Room](created.id) >>-
|
||||
reloadSiteSocket >>-
|
||||
lobbyReload
|
||||
lobbyReload
|
||||
}
|
||||
|
||||
def finish(started: Started): Fu[Tournament] = started.readyToFinish.fold({
|
||||
|
|
|
@ -12,6 +12,7 @@ case class User(
|
|||
progress: Int,
|
||||
perfs: Perfs,
|
||||
count: Count,
|
||||
artificial: Boolean = false,
|
||||
troll: Boolean = false,
|
||||
ipBan: Boolean = false,
|
||||
enabled: Boolean,
|
||||
|
@ -67,6 +68,7 @@ object User {
|
|||
val username = "username"
|
||||
val rating = "rating"
|
||||
val progress = "progress"
|
||||
val artificial = "artificial"
|
||||
val perfs = "perfs"
|
||||
val count = "count"
|
||||
val troll = "troll"
|
||||
|
@ -100,6 +102,7 @@ object User {
|
|||
progress = r intD progress,
|
||||
perfs = r.getO[Perfs](perfs) | Perfs.default,
|
||||
count = r.get[Count](count),
|
||||
artificial = r boolD artificial,
|
||||
troll = r boolD troll,
|
||||
ipBan = r boolD ipBan,
|
||||
enabled = r bool enabled,
|
||||
|
@ -118,6 +121,7 @@ object User {
|
|||
progress -> w.int(o.progress),
|
||||
perfs -> o.perfs,
|
||||
count -> o.count,
|
||||
artificial -> w.boolO(o.artificial),
|
||||
troll -> w.boolO(o.troll),
|
||||
ipBan -> w.boolO(o.ipBan),
|
||||
enabled -> o.enabled,
|
||||
|
|
|
@ -156,7 +156,8 @@ trait UserRepo {
|
|||
}
|
||||
}
|
||||
|
||||
def nameExists(username: String): Fu[Boolean] = $count exists normalize(username)
|
||||
def nameExists(username: String): Fu[Boolean] = idExists(normalize(username))
|
||||
def idExists(id: String): Fu[Boolean] = $count exists id
|
||||
|
||||
def engineIds: Fu[Set[String]] = $primitive(Json.obj("engine" -> true), "_id")(_.asOpt[String]) map (_.toSet)
|
||||
|
||||
|
@ -184,6 +185,7 @@ trait UserRepo {
|
|||
def updateTroll(user: User) = $update.field(user.id, "troll", user.troll)
|
||||
|
||||
def isEngine(id: ID): Fu[Boolean] = $count.exists($select(id) ++ Json.obj("engine" -> true))
|
||||
def isArtificial(id: ID): Fu[Boolean] = $count.exists($select(id) ++ Json.obj("artificial" -> true))
|
||||
|
||||
def setRoles(id: ID, roles: List[String]) = $update.field(id, "roles", roles)
|
||||
|
||||
|
@ -257,6 +259,33 @@ trait UserRepo {
|
|||
BSONFields.seenAt -> DateTime.now)
|
||||
}
|
||||
|
||||
def artificialSetPassword(id: String, password: String) =
|
||||
passwd(id, password) >> $update($select(id), $unset("artificial") ++ $set("enabled" -> true))
|
||||
|
||||
def insertArtificialUser(username: String, perfs: Perfs, progress: Int, createdAt: DateTime) = $insert bson {
|
||||
|
||||
val password = ornicar.scalalib.Random nextString 8
|
||||
val salt = ornicar.scalalib.Random nextString 32
|
||||
implicit def countHandler = Count.tube.handler
|
||||
implicit def perfsHandler = Perfs.tube.handler
|
||||
import lila.db.BSON.BSONJodaDateTimeHandler
|
||||
import User.BSONFields
|
||||
|
||||
BSONDocument(
|
||||
BSONFields.id -> normalize(username),
|
||||
BSONFields.username -> username,
|
||||
"artificial" -> true,
|
||||
"password" -> hash(password, salt),
|
||||
"salt" -> salt,
|
||||
BSONFields.perfs -> perfs,
|
||||
BSONFields.rating -> perfs.global.glicko.intRating,
|
||||
BSONFields.progress -> progress,
|
||||
BSONFields.count -> Count.default,
|
||||
BSONFields.enabled -> false,
|
||||
BSONFields.createdAt -> createdAt,
|
||||
BSONFields.seenAt -> createdAt)
|
||||
}
|
||||
|
||||
private def hash(pass: String, salt: String): String = "%s{%s}".format(pass, salt).sha1
|
||||
private def hash512(pass: String, salt: String): String = "%s{%s}".format(pass, salt).sha512
|
||||
}
|
||||
|
|
|
@ -2402,14 +2402,14 @@ var storage = {
|
|||
function ratingLog(a) {
|
||||
return Math.log(a / 150 + 1);
|
||||
}
|
||||
var rating = Math.max(800, Math.min(2000, e || 1200));
|
||||
var rating = Math.max(800, Math.min(2800, e || 1500));
|
||||
var ratio;
|
||||
if (rating == 1200) {
|
||||
if (rating == 1500) {
|
||||
ratio = 0.25;
|
||||
} else if (rating > 1200) {
|
||||
ratio = 0.25 + (ratingLog(rating - 1200) / ratingLog(800)) * 3 / 4;
|
||||
} else if (rating > 1500) {
|
||||
ratio = 0.25 + (ratingLog(rating - 1500) / ratingLog(1300)) * 3 / 4;
|
||||
} else {
|
||||
ratio = 0.25 - (ratingLog(1200 - rating) / ratingLog(400)) / 4;
|
||||
ratio = 0.25 - (ratingLog(1500 - rating) / ratingLog(500)) / 4;
|
||||
}
|
||||
return Math.round(ratio * 489);
|
||||
}
|
||||
|
@ -2463,7 +2463,7 @@ var storage = {
|
|||
}
|
||||
|
||||
$('#hooks_chart').append(
|
||||
_.map([1000, 1200, 1300, 1400, 1600, 1800, 2000], function(v) {
|
||||
_.map([1000, 1200, 1400, 1500, 1600, 1800, 2000, 2200, 2400, 2600, 2800], function(v) {
|
||||
var b = ratingY(v);
|
||||
return '<span class="y label" style="bottom:' + (b + 5) + 'px">' + v + '</span>' +
|
||||
'<div class="grid horiz" style="height:' + (b + 4) + 'px"></div>';
|
||||
|
|
5
todo
5
todo
|
@ -102,6 +102,11 @@ wrong analysis = move 9 should be a blunder http://en.lichess.org/analyse/pf3ruf
|
|||
check "continue from position" #lichess
|
||||
from position + clock = fail (due to turns != played turns)
|
||||
streamed export of PGN http://en.lichess.org/forum/lichess-feedback/download-all-games#5
|
||||
fix clipping in lobby chart
|
||||
visual sep under standard/chess960 ratings
|
||||
progress over date ranges
|
||||
get compiled js back to public/compiled
|
||||
rating range restriction is broken on lobby
|
||||
|
||||
--- deploy
|
||||
|
||||
|
|
Loading…
Reference in New Issue