2016-02-08 08:42:33 -07:00
|
|
|
package lila.explorer
|
|
|
|
|
|
|
|
import scala.util.{ Success, Failure }
|
|
|
|
|
|
|
|
import chess.variant.Variant
|
2016-02-08 22:00:49 -07:00
|
|
|
import org.joda.time.DateTime
|
2016-02-08 08:42:33 -07:00
|
|
|
import play.api.libs.iteratee._
|
|
|
|
import play.api.libs.ws.{ WS, WSAuthScheme }
|
|
|
|
import play.api.Play.current
|
|
|
|
|
|
|
|
import lila.db.api._
|
|
|
|
import lila.db.Implicits._
|
|
|
|
import lila.game.BSONHandlers.gameBSONHandler
|
|
|
|
import lila.game.tube.gameTube
|
|
|
|
import lila.game.{ Game, GameRepo, Query, PgnDump, Player }
|
|
|
|
|
|
|
|
private final class ExplorerIndexer(endpoint: String) {
|
|
|
|
|
2016-02-08 22:00:49 -07:00
|
|
|
private val maxGames = Int.MaxValue
|
|
|
|
private val batchSize = 100
|
|
|
|
private val minRating = 1600
|
|
|
|
private val separator = "\n\n\n"
|
2016-02-08 08:42:33 -07:00
|
|
|
|
|
|
|
def apply(variantKey: String): Funit = Variant.byKey get variantKey match {
|
|
|
|
case None => fufail(s"Invalid variant $variantKey")
|
|
|
|
case Some(variant) =>
|
|
|
|
val url = s"$endpoint/lichess/${variant.key}"
|
|
|
|
val query = $query(
|
|
|
|
Query.rated ++
|
|
|
|
Query.finished ++
|
|
|
|
Query.turnsMoreThan(10) ++
|
|
|
|
Query.variant(variant) ++
|
|
|
|
(variant == chess.variant.Horde).??(Query.sinceHordePawnsAreWhite))
|
|
|
|
pimpQB(query)
|
|
|
|
.sort(Query.sortChronological)
|
|
|
|
.cursor[Game]()
|
|
|
|
.enumerate(maxGames, stopOnError = true) &>
|
|
|
|
Enumeratee.mapM[Game].apply[Option[String]](makeFastPgn) &>
|
|
|
|
Enumeratee.grouped(Iteratee takeUpTo batchSize) |>>>
|
2016-02-08 20:22:24 -07:00
|
|
|
Iteratee.foldM[Seq[Option[String]], (Int, Long)](0 -> nowMillis) {
|
|
|
|
case ((number, millis), pgnOptions) =>
|
2016-02-08 08:42:33 -07:00
|
|
|
val pgns = pgnOptions.flatten
|
2016-02-08 08:49:17 -07:00
|
|
|
WS.url(url).put(pgns mkString separator) andThen {
|
2016-02-08 20:22:24 -07:00
|
|
|
case Success(res) if res.status == 200 => logger.info(s"${number} ${nowMillis - millis} ms")
|
2016-02-08 08:42:33 -07:00
|
|
|
case Success(res) => logger.warn(s"[${res.status}]")
|
|
|
|
case Failure(err) => logger.warn(s"$err")
|
2016-02-08 20:22:24 -07:00
|
|
|
} inject {
|
|
|
|
(number + pgns.size) -> nowMillis
|
|
|
|
}
|
2016-02-08 08:42:33 -07:00
|
|
|
} void
|
|
|
|
}
|
2016-02-08 22:00:49 -07:00
|
|
|
|
|
|
|
def apply(game: Game): Funit = makeFastPgn(game).flatMap {
|
|
|
|
_ ?? { pgn =>
|
2016-02-08 23:04:32 -07:00
|
|
|
WS.url(s"$endpoint/lichess/${game.variant.key}").put(pgn) andThen {
|
2016-02-08 22:00:49 -07:00
|
|
|
case Success(res) if res.status == 200 =>
|
|
|
|
case Success(res) => logger.warn(s"[${res.status}]")
|
|
|
|
case Failure(err) => logger.warn(s"$err")
|
|
|
|
} void
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private def valid(game: Game) =
|
|
|
|
game.finished &&
|
|
|
|
game.rated &&
|
|
|
|
game.turns >= 10 &&
|
|
|
|
game.variant != chess.variant.FromPosition
|
|
|
|
|
|
|
|
private def stableRating(player: Player) = player.rating ifFalse player.provisional
|
|
|
|
|
|
|
|
private def makeFastPgn(game: Game): Fu[Option[String]] = ~(for {
|
|
|
|
whiteRating <- stableRating(game.whitePlayer)
|
|
|
|
blackRating <- stableRating(game.blackPlayer)
|
2016-02-08 22:02:31 -07:00
|
|
|
if ((whiteRating + blackRating) / 2 > minRating)
|
2016-02-08 22:00:49 -07:00
|
|
|
if valid(game)
|
|
|
|
} yield GameRepo initialFen game map { initialFen =>
|
|
|
|
val fenTags = initialFen.?? { fen => List(s"[FEN $fen]") }
|
|
|
|
val otherTags = List(
|
|
|
|
s"[LichessID ${game.id}]",
|
|
|
|
s"[TimeControl ${game.clock.fold("-")(_.show)}]",
|
|
|
|
s"[WhiteElo $whiteRating]",
|
|
|
|
s"[BlackElo $blackRating]",
|
|
|
|
s"[Result ${PgnDump.result(game)}]")
|
|
|
|
val allTags = fenTags ::: otherTags
|
|
|
|
s"${allTags.mkString("\n")}\n\n${game.pgnMoves.mkString(" ")}".some
|
|
|
|
})
|
|
|
|
|
|
|
|
private val logger = play.api.Logger("explorer")
|
2016-02-08 08:42:33 -07:00
|
|
|
}
|