2014-12-23 14:45:37 -07:00
|
|
|
package controllers
|
|
|
|
|
|
|
|
import chess.format.Forsyth
|
2015-01-08 16:48:47 -07:00
|
|
|
import chess.format.Forsyth.SituationPlus
|
2015-01-11 07:23:25 -07:00
|
|
|
import chess.Situation
|
2016-03-26 02:06:38 -06:00
|
|
|
import chess.variant.{ Variant, Standard, FromPosition }
|
2016-02-24 22:41:27 -07:00
|
|
|
import play.api.i18n.Messages.Implicits._
|
2014-12-23 14:45:37 -07:00
|
|
|
import play.api.libs.json.Json
|
2015-09-16 09:23:52 -06:00
|
|
|
import play.api.mvc._
|
2015-12-15 09:34:32 -07:00
|
|
|
import scala.concurrent.duration._
|
2014-12-23 14:45:37 -07:00
|
|
|
|
2017-09-11 10:06:32 -06:00
|
|
|
import lila.api.Context
|
2014-12-23 14:45:37 -07:00
|
|
|
import lila.app._
|
2017-09-27 07:43:27 -06:00
|
|
|
import lila.common.PimpedJson._
|
2017-09-11 10:06:32 -06:00
|
|
|
import lila.game.{ GameRepo, Pov, PgnDump }
|
2017-05-26 09:15:15 -06:00
|
|
|
import lila.i18n.I18nKeys
|
2015-12-06 03:23:39 -07:00
|
|
|
import lila.round.Forecast.{ forecastStepJsonFormat, forecastJsonWriter }
|
2017-09-11 10:06:32 -06:00
|
|
|
import lila.round.JsonView.WithFlags
|
2014-12-23 14:45:37 -07:00
|
|
|
import views._
|
|
|
|
|
2015-09-16 15:03:56 -06:00
|
|
|
object UserAnalysis extends LilaController with TheftPrevention {
|
2014-12-23 14:45:37 -07:00
|
|
|
|
2016-02-12 05:38:49 -07:00
|
|
|
def index = load("", Standard)
|
2014-12-23 14:45:37 -07:00
|
|
|
|
2017-09-09 09:01:35 -06:00
|
|
|
def parse(arg: String) = arg.split("/", 2) match {
|
2016-02-24 20:58:45 -07:00
|
|
|
case Array(key) => load("", Variant orDefault key)
|
2016-02-24 21:10:46 -07:00
|
|
|
case Array(key, fen) => Variant.byKey get key match {
|
2017-02-14 08:34:07 -07:00
|
|
|
case Some(variant) => load(fen, variant)
|
2016-03-26 02:06:38 -06:00
|
|
|
case _ if fen == Standard.initialFen => load(arg, Standard)
|
2017-02-14 08:34:07 -07:00
|
|
|
case _ => load(arg, FromPosition)
|
2016-02-12 05:38:49 -07:00
|
|
|
}
|
2016-02-24 20:58:45 -07:00
|
|
|
case _ => load("", Standard)
|
|
|
|
}
|
2016-02-12 05:38:49 -07:00
|
|
|
|
|
|
|
def load(urlFen: String, variant: Variant) = Open { implicit ctx =>
|
2017-05-01 15:32:25 -06:00
|
|
|
val decodedFen = lila.common.String.decodeUriPath(urlFen)
|
|
|
|
.map(_.replace("_", " ").trim).filter(_.nonEmpty)
|
|
|
|
.orElse(get("fen"))
|
2016-04-18 00:32:34 -06:00
|
|
|
val pov = makePov(decodedFen, variant)
|
2015-07-02 09:45:09 -06:00
|
|
|
val orientation = get("color").flatMap(chess.Color.apply) | pov.color
|
2017-02-02 05:13:32 -07:00
|
|
|
Env.api.roundApi.userAnalysisJson(pov, ctx.pref, decodedFen, orientation, owner = false, me = ctx.me) map { data =>
|
2016-02-15 20:29:14 -07:00
|
|
|
Ok(html.board.userAnalysis(data, pov))
|
2014-12-23 14:45:37 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-03 08:33:24 -06:00
|
|
|
private lazy val keyboardI18nKeys = {
|
|
|
|
Seq(
|
2017-05-26 09:15:15 -06:00
|
|
|
I18nKeys.keyboardShortcuts,
|
|
|
|
I18nKeys.keyMoveBackwardOrForward,
|
|
|
|
I18nKeys.keyGoToStartOrEnd,
|
|
|
|
I18nKeys.keyShowOrHideComments,
|
|
|
|
I18nKeys.keyEnterOrExitVariation,
|
|
|
|
I18nKeys.youCanAlsoScrollOverTheBoardToMoveInTheGame,
|
2017-05-27 03:43:51 -06:00
|
|
|
I18nKeys.analysisShapesHowTo
|
2017-02-14 08:34:07 -07:00
|
|
|
)
|
2016-08-03 08:33:24 -06:00
|
|
|
}
|
|
|
|
|
2017-05-26 09:15:15 -06:00
|
|
|
def keyboardI18n = Open { implicit ctx =>
|
2017-07-15 16:40:00 -06:00
|
|
|
JsonOk(fuccess(lila.i18n.JsDump.keysToObject(keyboardI18nKeys, lila.i18n.I18nDb.Site, lang)))
|
2016-08-03 08:33:24 -06:00
|
|
|
}
|
|
|
|
|
2016-04-18 00:32:34 -06:00
|
|
|
private[controllers] def makePov(fen: Option[String], variant: Variant): Pov = makePov {
|
|
|
|
fen.filter(_.nonEmpty).flatMap {
|
|
|
|
Forsyth.<<<@(variant, _)
|
|
|
|
} | SituationPlus(Situation(variant), 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
private[controllers] def makePov(from: SituationPlus): Pov = Pov(
|
2014-12-23 14:45:37 -07:00
|
|
|
lila.game.Game.make(
|
2015-01-08 16:48:47 -07:00
|
|
|
game = chess.Game(
|
2017-06-30 13:06:37 -06:00
|
|
|
situation = from.situation,
|
2017-02-14 08:34:07 -07:00
|
|
|
turns = from.turns
|
|
|
|
),
|
2014-12-23 14:45:37 -07:00
|
|
|
whitePlayer = lila.game.Player.white,
|
|
|
|
blackPlayer = lila.game.Player.black,
|
|
|
|
mode = chess.Mode.Casual,
|
2016-02-12 05:38:49 -07:00
|
|
|
variant = from.situation.board.variant,
|
2014-12-23 14:45:37 -07:00
|
|
|
source = lila.game.Source.Api,
|
2017-02-14 08:34:07 -07:00
|
|
|
pgnImport = None
|
|
|
|
).copy(id = "synthetic"),
|
|
|
|
from.situation.color
|
|
|
|
)
|
2014-12-23 14:45:37 -07:00
|
|
|
|
2014-12-24 06:30:24 -07:00
|
|
|
def game(id: String, color: String) = Open { implicit ctx =>
|
2015-09-19 06:40:44 -06:00
|
|
|
OptionFuResult(GameRepo game id) { game =>
|
2017-09-11 10:06:32 -06:00
|
|
|
val pov = Pov(game, chess.Color(color == "white"))
|
2017-10-12 11:53:54 -06:00
|
|
|
if (game.replayable) negotiate(
|
2017-09-11 10:06:32 -06:00
|
|
|
html = fuccess(Redirect(routes.Round.watcher(game.id, color))),
|
|
|
|
api = apiVersion => mobileAnalysis(pov, apiVersion)
|
|
|
|
)
|
2017-03-07 05:00:16 -07:00
|
|
|
else GameRepo initialFen game.id flatMap { initialFen =>
|
2017-10-06 09:01:54 -06:00
|
|
|
Env.api.roundApi.userAnalysisJson(pov, ctx.pref, initialFen, pov.color, owner = isMyPov(pov), me = ctx.me) flatMap { data =>
|
|
|
|
negotiate(
|
|
|
|
html = Ok(html.board.userAnalysis(data, pov)).fuccess,
|
|
|
|
api = _ => Ok(data).fuccess
|
|
|
|
)
|
2015-05-07 03:11:53 -06:00
|
|
|
}
|
2015-09-19 06:40:44 -06:00
|
|
|
} map NoCache
|
2014-12-24 06:30:24 -07:00
|
|
|
}
|
|
|
|
}
|
2015-09-16 09:23:52 -06:00
|
|
|
|
2017-09-11 10:06:32 -06:00
|
|
|
private def mobileAnalysis(pov: Pov, apiVersion: lila.common.ApiVersion)(implicit ctx: Context): Fu[Result] =
|
|
|
|
GameRepo initialFen pov.game.id flatMap { initialFen =>
|
|
|
|
Game.preloadUsers(pov.game) zip
|
|
|
|
(Env.analyse.analyser get pov.game.id) zip
|
2017-09-27 07:43:27 -06:00
|
|
|
Env.game.crosstableApi.withMatchup(pov.game) zip
|
2017-09-18 11:28:56 -06:00
|
|
|
Env.bookmark.api.exists(pov.game, ctx.me) flatMap {
|
2017-09-11 10:06:32 -06:00
|
|
|
// case _ ~ analysis ~ analysisInProgress ~ simul ~ chat ~ crosstable ~ bookmarked ~ pgn =>
|
2017-09-27 07:43:27 -06:00
|
|
|
case _ ~ analysis ~ crosstable ~ bookmarked =>
|
|
|
|
import lila.game.JsonView.crosstableWithMatchupWrites
|
2017-09-11 10:06:32 -06:00
|
|
|
Env.api.roundApi.review(pov, apiVersion,
|
|
|
|
tv = none,
|
|
|
|
analysis,
|
|
|
|
initialFenO = initialFen.some,
|
2017-09-27 07:43:27 -06:00
|
|
|
withFlags = WithFlags(division = true, opening = true, clocks = true, movetimes = true)) map { data =>
|
|
|
|
Ok(data.add("crosstable", crosstable))
|
2017-09-11 10:06:32 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-02 05:13:32 -07:00
|
|
|
def socket = SocketOption { implicit ctx =>
|
|
|
|
getSocketUid("sri") ?? { uid =>
|
|
|
|
Env.analyse.socketHandler.join(uid, ctx.me) map some
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-24 22:41:27 -07:00
|
|
|
// XHR only
|
|
|
|
def pgn = OpenBody { implicit ctx =>
|
|
|
|
implicit val req = ctx.body
|
|
|
|
Env.importer.forms.importForm.bindFromRequest.fold(
|
|
|
|
failure => BadRequest(errorsAsJson(failure)).fuccess,
|
2016-02-24 23:29:49 -07:00
|
|
|
data => Env.importer.importer.inMemory(data).fold(
|
2016-05-09 08:53:09 -06:00
|
|
|
err => BadRequest(jsonError(err.shows)).fuccess, {
|
|
|
|
case (game, fen) =>
|
|
|
|
val pov = Pov(game, chess.White)
|
2017-02-02 05:13:32 -07:00
|
|
|
Env.api.roundApi.userAnalysisJson(pov, ctx.pref, initialFen = fen.map(_.value), pov.color, owner = false, me = ctx.me) map { data =>
|
2016-05-09 08:53:09 -06:00
|
|
|
Ok(data)
|
|
|
|
}
|
2017-02-14 08:34:07 -07:00
|
|
|
}
|
|
|
|
)
|
2016-02-24 23:29:49 -07:00
|
|
|
).map(_ as JSON)
|
2016-02-24 22:41:27 -07:00
|
|
|
}
|
|
|
|
|
2017-09-09 09:01:35 -06:00
|
|
|
def forecasts(fullId: String) = AuthBody(BodyParsers.parse.json) { implicit ctx => me =>
|
2017-02-14 08:34:07 -07:00
|
|
|
import lila.round.Forecast
|
|
|
|
OptionFuResult(GameRepo pov fullId) { pov =>
|
|
|
|
if (isTheft(pov)) fuccess(theftResponse)
|
|
|
|
else ctx.body.body.validate[Forecast.Steps].fold(
|
|
|
|
err => BadRequest(err.toString).fuccess,
|
|
|
|
forecasts => Env.round.forecastApi.save(pov, forecasts) >>
|
|
|
|
Env.round.forecastApi.loadForDisplay(pov) map {
|
|
|
|
case None => Ok(Json.obj("none" -> true))
|
|
|
|
case Some(fc) => Ok(Json toJson fc) as JSON
|
|
|
|
} recover {
|
|
|
|
case Forecast.OutOfSync => Ok(Json.obj("reload" -> true))
|
|
|
|
}
|
|
|
|
)
|
|
|
|
}
|
2015-09-16 09:23:52 -06:00
|
|
|
}
|
2015-12-06 03:23:39 -07:00
|
|
|
|
2017-09-09 09:01:35 -06:00
|
|
|
def forecastsOnMyTurn(fullId: String, uci: String) = AuthBody(BodyParsers.parse.json) { implicit ctx => me =>
|
2017-02-14 08:34:07 -07:00
|
|
|
import lila.round.Forecast
|
|
|
|
OptionFuResult(GameRepo pov fullId) { pov =>
|
|
|
|
if (isTheft(pov)) fuccess(theftResponse)
|
|
|
|
else {
|
|
|
|
ctx.body.body.validate[Forecast.Steps].fold(
|
|
|
|
err => BadRequest(err.toString).fuccess,
|
|
|
|
forecasts => {
|
|
|
|
def wait = 50 + (Forecast maxPlies forecasts min 10) * 50
|
|
|
|
Env.round.forecastApi.playAndSave(pov, uci, forecasts) >>
|
|
|
|
Env.current.scheduler.after(wait.millis) {
|
|
|
|
Ok(Json.obj("reload" -> true))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
)
|
2015-12-06 03:23:39 -07:00
|
|
|
}
|
2017-02-14 08:34:07 -07:00
|
|
|
}
|
2015-12-06 03:23:39 -07:00
|
|
|
}
|
2014-12-23 14:45:37 -07:00
|
|
|
}
|