new attempt at getting rid of stalled analysis

This commit is contained in:
Thibault Duplessis 2014-04-13 17:57:17 +02:00
parent e7f6e0a273
commit 6f29cc3272
8 changed files with 39 additions and 19 deletions

View file

@ -60,7 +60,7 @@ object Analyse extends LilaController {
case Some(i) => fuccess(i.pgn)
case None => for {
pgn Env.game.pgnDump(game)
analysis env.analyser get game.id
analysis env.analyser getDone game.id
} yield analysis.fold(pgn)(a => Env.analyse.annotator(pgn, a)).toString
}) flatMap { content =>
Env.game.pgnDump filename game map { filename =>

View file

@ -45,14 +45,13 @@ object Round extends LilaController with TheftPrevention {
PreventTheft(pov) {
env.version(pov.gameId) zip
pov.opponent.userId.??(UserRepo.isEngine) zip
(analyser has pov.gameId) zip
(pov.game.tournamentId ?? TournamentRepo.byId) zip
Env.game.crosstableApi(pov.game) zip
(pov.game.hasChat optionFu {
Env.chat.api.playerChat find pov.gameId map (_ forUser ctx.me)
}) map {
case (((((v, engine), analysed), tour), crosstable), chat) =>
Ok(html.round.player(pov, v, engine, analysed,
case ((((v, engine), tour), crosstable), chat) =>
Ok(html.round.player(pov, v, engine,
chat = chat, tour = tour, cross = crosstable))
}
},

View file

@ -1,4 +1,4 @@
@(pov: Pov, version: Int, engine: Boolean, analysed: Boolean, chat: Option[lila.chat.MixedChat], tour: Option[lila.tournament.Tournament], cross: Option[lila.game.Crosstable])(implicit ctx: Context)
@(pov: Pov, version: Int, engine: Boolean, chat: Option[lila.chat.MixedChat], tour: Option[lila.tournament.Tournament], cross: Option[lila.game.Crosstable])(implicit ctx: Context)
@import pov._

View file

@ -47,7 +47,7 @@
<td>@tournamentLink(tour)</td>
<td class="small">@tourMode(tour)</td>
<td data-icon="p">&nbsp;@tour.clock.show | @tour.durationString</td>
<td data-icon="r">&nbsp;@tour.nbPlayers</td>
<td data-icon="r">&nbsp;@tour.playerRatio</td>
<td>@joinButton(tour)</td>
</tr>
}

View file

@ -4,6 +4,7 @@ import scala.concurrent.Future
import akka.actor.ActorSelection
import akka.pattern.ask
import org.joda.time.DateTime
import chess.format.UciDump
import chess.Replay
@ -16,9 +17,15 @@ import tube.analysisTube
final class Analyser(ai: ActorSelection, indexer: ActorSelection) {
def get(id: String): Fu[Option[Analysis]] = AnalysisRepo doneById id
def get(id: String): Fu[Option[Analysis]] = AnalysisRepo byId id flatMap evictStalled
def has(id: String): Fu[Boolean] = AnalysisRepo isDone id
def getDone(id: String): Fu[Option[Analysis]] = AnalysisRepo doneById id flatMap evictStalled
def evictStalled(oa: Option[Analysis]): Fu[Option[Analysis]] = oa ?? { a =>
if (a.stalled) (AnalysisRepo remove a) inject none[Analysis] else fuccess(a.some)
}
def hasDone(id: String): Fu[Boolean] = getDone(id) map (_.isDefined)
def hasMany(ids: Seq[String]): Fu[Set[String]] =
$primitive[Analysis, String]($select byIds ids, "_id")(_.asOpt[String]) map (_.toSet)
@ -40,7 +47,7 @@ final class Analyser(ai: ActorSelection, indexer: ActorSelection) {
replay Replay(game.pgnMoves mkString " ", initialFen, game.variant).future
uciMoves = UciDump(replay)
infos ai ? lila.hub.actorApi.ai.Analyse(uciMoves, initialFen) mapTo manifest[List[Info]]
analysis = Analysis(id, infos, true)
analysis = Analysis(id, infos, true, DateTime.now)
} yield UciToPgn(replay, analysis)) flatFold (
e => fufail[Analysis](e.getMessage), {
case (a, errors) => {

View file

@ -3,16 +3,19 @@ package lila.analyse
import chess.Color
import chess.format.Nag
import org.joda.time.DateTime
case class Analysis(
id: String,
infos: List[Info],
done: Boolean,
date: DateTime,
old: Boolean = false) {
lazy val infoAdvices: InfoAdvices = {
(Info.start :: infos) sliding 2 collect {
case List(prev, info) => info -> {
(old || info.hasVariation) ?? Advice(prev, info)
(old || info.hasVariation) ?? Advice(prev, info)
}
}
}.toList
@ -24,7 +27,7 @@ case class Analysis(
i.best map { b => i.ply -> b.keys }
}).flatten.toMap
def encode: RawAnalysis = RawAnalysis(id, encodeInfos, done)
def encode: RawAnalysis = RawAnalysis(id, encodeInfos, done, date, old)
private def encodeInfos = Info encodeList infos
def summary: List[(Color, List[(Nag, Int)])] = Color.all map { color =>
@ -36,15 +39,17 @@ case class Analysis(
}
def valid = encodeInfos.replace(";", "").nonEmpty
def stalled = (done && !valid) || (!done && date.isBefore(DateTime.now minusMinutes 20))
}
object Analysis {
import lila.db.JsTube
import lila.db.JsTube, JsTube.Helpers._
import play.api.libs.json._
private[analyse] lazy val tube = JsTube(
reader = Reads[Analysis](js =>
reader = (__.json update readDate('date)) andThen Reads[Analysis](js =>
~(for {
obj js.asOpt[JsObject]
rawAnalysis RawAnalysis.tube.read(obj).asOpt
@ -53,16 +58,21 @@ object Analysis {
),
writer = Writes[Analysis](analysis =>
RawAnalysis.tube.write(analysis.encode) getOrElse JsUndefined("[db] Can't write analysis " + analysis.id)
)
) andThen (__.json update writeDate('date))
)
}
private[analyse] case class RawAnalysis(id: String, data: String, done: Boolean, old: Boolean = false) {
private[analyse] case class RawAnalysis(
id: String,
data: String,
done: Boolean,
date: DateTime,
old: Boolean = false) {
def decode: Option[Analysis] = (done, data) match {
case (true, "") => new Analysis(id, Nil, false, old).some
case (true, d) => Info decodeList d map { new Analysis(id, _, done, old) }
case (false, _) => new Analysis(id, Nil, false, old).some
case (true, "") => new Analysis(id, Nil, false, date, old).some
case (true, d) => Info decodeList d map { new Analysis(id, _, done, date, old) }
case (false, _) => new Analysis(id, Nil, false, date, old).some
}
}

View file

@ -30,6 +30,8 @@ object AnalysisRepo {
)) ++ $unset("old", "data"),
upsert = true)
def byId(id: ID): Fu[Option[Analysis]] = $find byId id
def doneById(id: ID): Fu[Option[Analysis]] =
$find.one($select(id) ++ Json.obj("done" -> true))
@ -59,4 +61,6 @@ object AnalysisRepo {
$find($query(Json.obj("done" -> true)) skip skip, nb)
def count = $count($select.all)
def remove(a: Analysis) = $remove byId a.id
}

View file

@ -28,7 +28,7 @@ private[gameSearch] final class Indexer(
case FinishGame(game, _, _) => self ! InsertGame(game)
case InsertGame(game) => if (storable(game)) {
analyser has game.id foreach { analysed =>
analyser hasDone game.id foreach { analysed =>
client execute store(game, analysed)
}
}