rewrite puzzle history - WIP
This commit is contained in:
parent
47861bc852
commit
67a9b06589
|
@ -37,7 +37,7 @@ object Puzzle extends LilaController {
|
|||
}
|
||||
|
||||
def home = Open { implicit ctx =>
|
||||
selectPuzzle(ctx.me) flatMap { puzzle =>
|
||||
selectPuzzle(ctx.me) flatMap { puzzle =>
|
||||
renderShow(puzzle, ctx.isAuth.fold("play", "try")) map { Ok(_) }
|
||||
}
|
||||
}
|
||||
|
@ -59,22 +59,6 @@ object Puzzle extends LilaController {
|
|||
infos => JsData(puzzle, infos, "play", animationDuration = env.AnimationDuration)
|
||||
}
|
||||
|
||||
def history = Auth { implicit ctx =>
|
||||
me =>
|
||||
env userInfos me flatMap { ui =>
|
||||
negotiate(
|
||||
html = XhrOnly {
|
||||
fuccess { Ok(views.html.puzzle.history(ui)) }
|
||||
},
|
||||
api = _ => fuccess {
|
||||
Ok(JsData history ui)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private val noMorePuzzleJson = jsonError("No more puzzles for you!")
|
||||
|
||||
// XHR load next play puzzle
|
||||
def newPuzzle = Open { implicit ctx =>
|
||||
XhrOnly {
|
||||
|
@ -84,22 +68,21 @@ object Puzzle extends LilaController {
|
|||
}
|
||||
}
|
||||
|
||||
def difficulty = AuthBody { implicit ctx =>
|
||||
me =>
|
||||
implicit val req = ctx.body
|
||||
env.forms.difficulty.bindFromRequest.fold(
|
||||
err => fuccess(BadRequest(errorsAsJson(err))),
|
||||
value => Env.pref.api.setPref(
|
||||
me,
|
||||
(p: lila.pref.Pref) => p.copy(puzzleDifficulty = value),
|
||||
notifyChange = false) >> {
|
||||
reqToCtx(ctx.req) flatMap { newCtx =>
|
||||
selectPuzzle(newCtx.me) zip env.userInfos(newCtx.me) map {
|
||||
case (puzzle, infos) => Ok(JsData(puzzle, infos, ctx.isAuth.fold("play", "try"), animationDuration = env.AnimationDuration)(newCtx))
|
||||
}
|
||||
def difficulty = AuthBody { implicit ctx => me =>
|
||||
implicit val req = ctx.body
|
||||
env.forms.difficulty.bindFromRequest.fold(
|
||||
err => fuccess(BadRequest(errorsAsJson(err))),
|
||||
value => Env.pref.api.setPref(
|
||||
me,
|
||||
(p: lila.pref.Pref) => p.copy(puzzleDifficulty = value),
|
||||
notifyChange = false) >> {
|
||||
reqToCtx(ctx.req) flatMap { newCtx =>
|
||||
selectPuzzle(newCtx.me) zip env.userInfos(newCtx.me) map {
|
||||
case (puzzle, infos) => Ok(JsData(puzzle, infos, ctx.isAuth.fold("play", "try"), animationDuration = env.AnimationDuration)(newCtx))
|
||||
}
|
||||
}
|
||||
) map (_ as JSON)
|
||||
}
|
||||
) map (_ as JSON)
|
||||
}
|
||||
|
||||
private def selectPuzzle(user: Option[UserModel]) =
|
||||
|
@ -125,9 +108,9 @@ object Puzzle extends LilaController {
|
|||
case ((p2, infos), vote) => Ok {
|
||||
JsData(p2 | puzzle, infos, "view",
|
||||
voted = (vote match {
|
||||
case Some(_) => true
|
||||
case _ => false
|
||||
}) some,
|
||||
case Some(_) => true
|
||||
case _ => false
|
||||
}) some,
|
||||
round = newAttempt.some,
|
||||
animationDuration = env.AnimationDuration)
|
||||
}
|
||||
|
@ -151,20 +134,19 @@ object Puzzle extends LilaController {
|
|||
}
|
||||
}
|
||||
|
||||
def vote(id: PuzzleId) = AuthBody { implicit ctx =>
|
||||
me =>
|
||||
implicit val req = ctx.body
|
||||
env.forms.vote.bindFromRequest.fold(
|
||||
err => fuccess(BadRequest(errorsAsJson(err))),
|
||||
vote => env.api.vote.find(id, me) flatMap {
|
||||
v => env.api.vote.update(id, me, v, vote == 1)
|
||||
} map {
|
||||
case (p, a) =>
|
||||
if (vote == 1) lila.mon.puzzle.vote.up()
|
||||
else lila.mon.puzzle.vote.down()
|
||||
Ok(play.api.libs.json.Json.arr(a.vote, p.vote.sum))
|
||||
}
|
||||
) map (_ as JSON)
|
||||
def vote(id: PuzzleId) = AuthBody { implicit ctx => me =>
|
||||
implicit val req = ctx.body
|
||||
env.forms.vote.bindFromRequest.fold(
|
||||
err => fuccess(BadRequest(errorsAsJson(err))),
|
||||
vote => env.api.vote.find(id, me) flatMap {
|
||||
v => env.api.vote.update(id, me, v, vote == 1)
|
||||
} map {
|
||||
case (p, a) =>
|
||||
if (vote == 1) lila.mon.puzzle.vote.up()
|
||||
else lila.mon.puzzle.vote.down()
|
||||
Ok(play.api.libs.json.Json.arr(a.vote, p.vote.sum))
|
||||
}
|
||||
) map (_ as JSON)
|
||||
}
|
||||
|
||||
def recentGame = Action.async { req =>
|
||||
|
|
|
@ -10,19 +10,9 @@ import lila.puzzle._
|
|||
|
||||
object JsData extends lila.Steroids {
|
||||
|
||||
def history(infos: UserInfos) = Json.obj(
|
||||
"attempts" -> infos.history.map { a =>
|
||||
Json.obj(
|
||||
"puzzleId" -> a.puzzleId,
|
||||
"date" -> a.date,
|
||||
"win" -> a.win,
|
||||
"rating" -> a.rating,
|
||||
"ratingDiff" -> a.ratingDiff)
|
||||
})
|
||||
|
||||
def apply(
|
||||
puzzle: Puzzle,
|
||||
userInfos: Option[lila.puzzle.UserInfos],
|
||||
userInfos: Option[UserInfos],
|
||||
mode: String,
|
||||
animationDuration: scala.concurrent.duration.Duration,
|
||||
round: Option[Round] = None,
|
||||
|
@ -75,7 +65,9 @@ object JsData extends lila.Steroids {
|
|||
"user" -> userInfos.map { i =>
|
||||
Json.obj(
|
||||
"rating" -> i.user.perfs.puzzle.intRating,
|
||||
"history" -> i.history.nonEmpty.option(Json.toJson(i.chart))
|
||||
"history" -> i.history.map { r =>
|
||||
Json.arr(r.puzzleId, r.ratingDiff)
|
||||
}
|
||||
)
|
||||
},
|
||||
"difficulty" -> ctx.isAuth.option {
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
@(userInfos: lila.puzzle.UserInfos)(implicit ctx: Context)
|
||||
|
||||
<div class="undertable">
|
||||
<div class="undertable_top">
|
||||
<span class="title">@trans.recentlyPlayedPuzzles()</span>
|
||||
</div>
|
||||
<div class="undertable_inner scroll-shadow-soft">
|
||||
<div class="content">
|
||||
<table>
|
||||
<tbody>
|
||||
@userInfos.history.map { attempt =>
|
||||
<tr>
|
||||
<td>
|
||||
<a class="user_link ulpt" href="@routes.User.show(userInfos.user.username)">
|
||||
@userInfos.user.username (@attempt.rating) @showRatingDiff(attempt.ratingDiff)
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a class="user_link" href="@routes.Puzzle.show(attempt.puzzleId)">
|
||||
@trans.puzzleId(attempt.puzzleId)
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -104,7 +104,6 @@ POST /training/coordinate/color controllers.Coordinate.color
|
|||
GET /training controllers.Puzzle.home
|
||||
POST /training/difficulty controllers.Puzzle.difficulty
|
||||
GET /training/new controllers.Puzzle.newPuzzle
|
||||
GET /training/history controllers.Puzzle.history
|
||||
GET /training/daily controllers.Puzzle.daily
|
||||
GET /training/embed controllers.Puzzle.embed
|
||||
GET /training/frame controllers.Puzzle.frame
|
||||
|
|
|
@ -271,7 +271,6 @@ findTheBestMoveForWhite=Find the best move for white.
|
|||
findTheBestMoveForBlack=Find the best move for black.
|
||||
toTrackYourProgress=To track your progress:
|
||||
trainingSignupExplanation=Lichess will provide puzzles that match your ability, making for better training sessions.
|
||||
recentlyPlayedPuzzles=Recently played puzzles
|
||||
puzzleId=Puzzle %s
|
||||
puzzleOfTheDay=Puzzle of the day
|
||||
clickToSolve=Click to solve
|
||||
|
|
|
@ -19,10 +19,12 @@ case class Round(
|
|||
|
||||
object Round {
|
||||
|
||||
case class Mini(puzzleId: Int, ratingDiff: Int)
|
||||
|
||||
object BSONFields {
|
||||
val puzzleId = "p"
|
||||
val userId = "u"
|
||||
val date = "d"
|
||||
val date = "a"
|
||||
val win = "w"
|
||||
val rating = "r"
|
||||
val ratingDiff = "d"
|
||||
|
@ -30,8 +32,9 @@ object Round {
|
|||
|
||||
import reactivemongo.bson._
|
||||
import lila.db.BSON
|
||||
import lila.db.dsl._
|
||||
import BSON.BSONJodaDateTimeHandler
|
||||
implicit val roundBSONHandler = new BSON[Round] {
|
||||
implicit val RoundBSONHandler = new BSON[Round] {
|
||||
|
||||
import BSONFields._
|
||||
|
||||
|
@ -51,4 +54,11 @@ object Round {
|
|||
rating -> w.int(o.rating),
|
||||
ratingDiff -> w.int(o.ratingDiff))
|
||||
}
|
||||
|
||||
private[puzzle] implicit val RoundMiniBSONReader = new BSONDocumentReader[Mini] {
|
||||
import BSONFields._
|
||||
def read(doc: Bdoc): Mini = Mini(
|
||||
puzzleId = doc.getAs[Int](puzzleId) err "RoundMini no puzzleId",
|
||||
ratingDiff = doc.getAs[Int](ratingDiff) err "RoundMini no ratingDiff")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,52 +1,37 @@
|
|||
package lila.puzzle
|
||||
|
||||
import play.api.libs.json._
|
||||
import reactivemongo.bson._
|
||||
|
||||
import lila.db.dsl._
|
||||
import lila.rating.Glicko
|
||||
import lila.user.User
|
||||
|
||||
case class UserInfos(user: User, history: List[Round], chart: JsArray)
|
||||
case class UserInfos(user: User, history: List[Round.Mini])
|
||||
|
||||
object UserInfos {
|
||||
|
||||
private def historySize = 20
|
||||
private def historySize = 12
|
||||
private def chartSize = 12
|
||||
|
||||
import Round.roundBSONHandler
|
||||
|
||||
lazy val defaultChart = JsArray {
|
||||
List.fill(chartSize)(Glicko.default.intRating) map { JsNumber(_) }
|
||||
}
|
||||
import Round.RoundMiniBSONReader
|
||||
|
||||
def apply(roundColl: Coll) = new {
|
||||
|
||||
def apply(user: User): Fu[UserInfos] = fetchRounds(user.id) map { rounds =>
|
||||
new UserInfos(user, makeHistory(rounds), makeChart(rounds))
|
||||
} recover {
|
||||
case e: Exception =>
|
||||
logger.error("user infos", e)
|
||||
new UserInfos(user, Nil, JsArray())
|
||||
def apply(user: User): Fu[UserInfos] = fetchRoundMinis(user.id) map {
|
||||
new UserInfos(user, _)
|
||||
}
|
||||
|
||||
def apply(user: Option[User]): Fu[Option[UserInfos]] =
|
||||
user ?? { apply(_) map (_.some) }
|
||||
|
||||
private def fetchRounds(userId: String): Fu[List[Round]] =
|
||||
roundColl.find(BSONDocument(
|
||||
Round.BSONFields.userId -> userId
|
||||
)).sort(BSONDocument(
|
||||
Round.BSONFields.date -> -1
|
||||
)).cursor[Round]()
|
||||
.gather[List](math.max(historySize, chartSize))
|
||||
}
|
||||
|
||||
private def makeHistory(rounds: List[Round]) = rounds.take(historySize)
|
||||
|
||||
private def makeChart(rounds: List[Round]) = JsArray {
|
||||
val ratings = rounds.take(chartSize).reverse map (_.userPostRating)
|
||||
val filled = List.fill(chartSize - ratings.size)(Glicko.default.intRating) ::: ratings
|
||||
filled map { JsNumber(_) }
|
||||
private def fetchRoundMinis(userId: String): Fu[List[Round.Mini]] =
|
||||
roundColl.find(
|
||||
$doc(Round.BSONFields.userId -> userId),
|
||||
$doc(
|
||||
"_id" -> false,
|
||||
Round.BSONFields.puzzleId -> true,
|
||||
Round.BSONFields.ratingDiff -> true
|
||||
)).sort($sort desc Round.BSONFields.date)
|
||||
.cursor[Round.Mini]()
|
||||
.gather[List](historySize atLeast chartSize)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,8 @@ module.exports = function(cfg, router, i18n) {
|
|||
|
||||
this.data = data(cfg);
|
||||
|
||||
console.log(this.data);
|
||||
|
||||
var userMove = function(orig, dest) {
|
||||
var res = puzzle.tryMove(this.data, [orig, dest]);
|
||||
var newProgress = res[0];
|
||||
|
|
|
@ -272,15 +272,12 @@ function renderFooter(ctrl) {
|
|||
function renderHistory(ctrl) {
|
||||
return m('div.history', {
|
||||
config: function(el, isUpdate, context) {
|
||||
var hash = ctrl.data.user.history.join('');
|
||||
var hash = ctrl.data.user.history.reduce(function(h, r) {
|
||||
return h + r.puzzleId;
|
||||
}, '');
|
||||
if (hash == context.hash) return;
|
||||
context.hash = hash;
|
||||
$.ajax({
|
||||
url: '/training/history',
|
||||
success: function(html) {
|
||||
el.innerHTML = html;
|
||||
}
|
||||
});
|
||||
el.innerHTML = JSON.stringify(ctrl.data.user.history);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue