diff --git a/app/analyse/AnalyseEnv.scala b/app/analyse/AnalyseEnv.scala
index ecf8e89fde..df3fd4f634 100644
--- a/app/analyse/AnalyseEnv.scala
+++ b/app/analyse/AnalyseEnv.scala
@@ -29,4 +29,14 @@ final class AnalyseEnv(
analysisRepo = analysisRepo,
gameRepo = gameRepo,
generator = generator)
+
+ lazy val paginator = new PaginatorBuilder(
+ analysisRepo = analysisRepo,
+ cached = cached,
+ gameRepo = gameRepo,
+ maxPerPage = GamePaginatorMaxPerPage)
+
+ lazy val cached = new Cached(
+ analysisRepo = analysisRepo,
+ nbTtl = AnalyseCachedNbTtl)
}
diff --git a/app/analyse/AnalysisRepo.scala b/app/analyse/AnalysisRepo.scala
index 0210accd7f..9e3da2bea5 100644
--- a/app/analyse/AnalysisRepo.scala
+++ b/app/analyse/AnalysisRepo.scala
@@ -7,7 +7,7 @@ import com.mongodb.casbah.query.Imports._
import scalaz.effects._
import org.joda.time.DateTime
-final class AnalysisRepo(collection: MongoCollection) {
+final class AnalysisRepo(val collection: MongoCollection) {
def done(id: String, a: Analysis) = io {
collection.update(
diff --git a/app/analyse/Cached.scala b/app/analyse/Cached.scala
new file mode 100644
index 0000000000..f9cc08aa31
--- /dev/null
+++ b/app/analyse/Cached.scala
@@ -0,0 +1,28 @@
+package lila
+package analyse
+
+import akka.util.duration._
+
+import memo.ActorMemo
+
+final class Cached(
+ analysisRepo: AnalysisRepo,
+ nbTtl: Int) {
+
+ import Cached._
+
+ def nbAnalysis: Int = memo(NbAnalysis)
+
+ private val memo = ActorMemo(loadFromDb, nbTtl, 5.seconds)
+
+ private def loadFromDb(key: Key) = key match {
+ case NbAnalysis ⇒ analysisRepo.collection.count.toInt
+ }
+}
+
+object Cached {
+
+ sealed trait Key
+
+ case object NbAnalysis extends Key
+}
diff --git a/app/analyse/PaginatorBuilder.scala b/app/analyse/PaginatorBuilder.scala
new file mode 100644
index 0000000000..49fe3a6beb
--- /dev/null
+++ b/app/analyse/PaginatorBuilder.scala
@@ -0,0 +1,43 @@
+package lila
+package analyse
+
+import game.{ DbGame, GameRepo }
+
+import com.github.ornicar.paginator._
+import com.mongodb.casbah.Imports._
+import org.joda.time.DateTime
+
+final class PaginatorBuilder(
+ analysisRepo: AnalysisRepo,
+ cached: Cached,
+ gameRepo: GameRepo,
+ maxPerPage: Int) {
+
+ def games(page: Int): Paginator[DbGame] = {
+ paginator(GameAdapter, page)
+ }
+
+ private def paginator(adapter: Adapter[DbGame], page: Int): Paginator[DbGame] =
+ Paginator(
+ adapter,
+ currentPage = page,
+ maxPerPage = maxPerPage
+ ).fold(_ ⇒ paginator(adapter, 0), identity)
+
+ private object GameAdapter extends Adapter[DbGame] {
+
+ def nbResults: Int = cached.nbAnalysis
+
+ def slice(offset: Int, length: Int): Seq[DbGame] = {
+ val ids = ((analysisRepo.collection.find(query, select) sort sort skip offset limit length).toList map {
+ _.getAs[String]("_id")
+ }).flatten
+ val games = (gameRepo games ids).unsafePerformIO
+ ids map { id ⇒ games find (_.id == id) }
+ } flatten
+
+ private def query = DBObject("done" -> true)
+ private def select = DBObject("_id" -> true)
+ private def sort = DBObject("date" -> -1)
+ }
+}
diff --git a/app/controllers/Game.scala b/app/controllers/Game.scala
index 349a42ccc3..027b45df3e 100644
--- a/app/controllers/Game.scala
+++ b/app/controllers/Game.scala
@@ -10,7 +10,9 @@ object Game extends LilaController {
val gameRepo = env.game.gameRepo
val paginator = env.game.paginator
+ val analysePaginator = env.analyse.paginator
val cached = env.game.cached
+ val analyseCached = env.analyse.cached
val bookmarkApi = env.bookmark.api
val listMenu = env.game.listMenu
@@ -47,6 +49,12 @@ object Game extends LilaController {
}
}
+ def analysed(page: Int) = Open { implicit ctx ⇒
+ reasonable(page) {
+ Ok(html.game.analysed(analysePaginator games page, makeListMenu))
+ }
+ }
+
def featuredJs(id: String) = Open { implicit ctx ⇒
IOk(gameRepo game id map { gameOption =>
html.game.featuredJs(gameOption)
@@ -57,5 +65,5 @@ object Game extends LilaController {
(page < maxPage).fold(result, BadRequest("too old"))
private def makeListMenu(implicit ctx: Context) =
- listMenu(bookmarkApi.countByUser, ctx.me)
+ listMenu(bookmarkApi.countByUser, analyseCached.nbAnalysis, ctx.me)
}
diff --git a/app/core/Settings.scala b/app/core/Settings.scala
index af7eed1461..3f68586ded 100644
--- a/app/core/Settings.scala
+++ b/app/core/Settings.scala
@@ -28,6 +28,8 @@ final class Settings(config: Config) {
val GameCachedNbTtl = millis("game.cached.nb.ttl")
val GamePaginatorMaxPerPage = getInt("game.paginator.max_per_page")
+ val AnalyseCachedNbTtl = millis("analyse.cached.nb.ttl")
+
val UserPaginatorMaxPerPage = getInt("user.paginator.max_per_page")
val UserEloUpdaterFloor = getInt("user.elo_updater.floor")
val UserCachedNbTtl = millis("user.cached.nb.ttl")
diff --git a/app/game/ListMenu.scala b/app/game/ListMenu.scala
index db590edc9f..92b5634931 100644
--- a/app/game/ListMenu.scala
+++ b/app/game/ListMenu.scala
@@ -9,16 +9,18 @@ case class ListMenu(
nbGames: Int,
nbMates: Int,
nbPopular: Int,
- nbBookmarks: Option[Int])
+ nbBookmarks: Option[Int],
+ nbAnalysed: Int)
object ListMenu {
type CountBookmarks = User ⇒ Int
- def apply(cached: Cached)(countBookmarks: CountBookmarks, me: Option[User]): ListMenu =
+ def apply(cached: Cached)(countBookmarks: CountBookmarks, countAnalysed: ⇒ Int, me: Option[User]): ListMenu =
new ListMenu(
nbGames = cached.nbGames,
nbMates = cached.nbMates,
nbPopular = cached.nbPopular,
- nbBookmarks = me map countBookmarks)
+ nbBookmarks = me map countBookmarks,
+ nbAnalysed = countAnalysed)
}
diff --git a/app/i18n/I18nKeys.scala b/app/i18n/I18nKeys.scala
index a109ed841c..f9442aeca9 100644
--- a/app/i18n/I18nKeys.scala
+++ b/app/i18n/I18nKeys.scala
@@ -82,6 +82,7 @@ final class I18nKeys(translator: Translator) {
val viewNbCheckmates = new Key("viewNbCheckmates")
val nbBookmarks = new Key("nbBookmarks")
val nbPopularGames = new Key("nbPopularGames")
+ val nbAnalysedGames = new Key("nbAnalysedGames")
val bookmarkedByNbPlayers = new Key("bookmarkedByNbPlayers")
val viewInFullSize = new Key("viewInFullSize")
val logOut = new Key("logOut")
@@ -162,5 +163,5 @@ final class I18nKeys(translator: Translator) {
val toggleBackground = new Key("toggleBackground")
val freeOnlineChessGamePlayChessNowInACleanInterfaceNoRegistrationNoAdsNoPluginRequiredPlayChessWithComputerFriendsOrRandomOpponents = new Key("freeOnlineChessGamePlayChessNowInACleanInterfaceNoRegistrationNoAdsNoPluginRequiredPlayChessWithComputerFriendsOrRandomOpponents")
- def keys = List(playWithAFriend, inviteAFriendToPlayWithYou, playWithTheMachine, challengeTheArtificialIntelligence, toInviteSomeoneToPlayGiveThisUrl, gameOver, waitingForOpponent, waiting, yourTurn, aiNameLevelAiLevel, level, toggleTheChat, toggleSound, chat, resign, checkmate, stalemate, white, black, createAGame, noGameAvailableRightNowCreateOne, whiteIsVictorious, blackIsVictorious, playWithTheSameOpponentAgain, newOpponent, playWithAnotherOpponent, yourOpponentWantsToPlayANewGameWithYou, joinTheGame, whitePlays, blackPlays, theOtherPlayerHasLeftTheGameYouCanForceResignationOrWaitForHim, makeYourOpponentResign, forceResignation, talkInChat, theFirstPersonToComeOnThisUrlWillPlayWithYou, whiteCreatesTheGame, blackCreatesTheGame, whiteJoinsTheGame, blackJoinsTheGame, whiteResigned, blackResigned, whiteLeftTheGame, blackLeftTheGame, shareThisUrlToLetSpectatorsSeeTheGame, youAreViewingThisGameAsASpectator, replayAndAnalyse, viewGameStats, flipBoard, threefoldRepetition, claimADraw, offerDraw, draw, nbConnectedPlayers, talkAboutChessAndDiscussLichessFeaturesInTheForum, seeTheGamesBeingPlayedInRealTime, gamesBeingPlayedRightNow, viewAllNbGames, viewNbCheckmates, nbBookmarks, nbPopularGames, bookmarkedByNbPlayers, viewInFullSize, logOut, signIn, signUp, people, games, forum, chessPlayers, minutesPerSide, variant, timeControl, start, username, password, haveAnAccount, allYouNeedIsAUsernameAndAPassword, learnMoreAboutLichess, rank, gamesPlayed, declineInvitation, cancel, timeOut, drawOfferSent, drawOfferDeclined, drawOfferAccepted, drawOfferCanceled, yourOpponentOffersADraw, accept, decline, playingRightNow, abortGame, gameAborted, standard, unlimited, mode, casual, rated, thisGameIsRated, rematch, rematchOfferSent, rematchOfferAccepted, rematchOfferCanceled, rematchOfferDeclined, cancelRematchOffer, viewRematch, play, inbox, chatRoom, spectatorRoom, composeMessage, sentMessages, incrementInSeconds, freeOnlineChess, spectators, nbWins, nbLosses, nbDraws, exportGames, color, eloRange, giveNbSeconds, searchAPlayer, whoIsOnline, allPlayers, namedPlayers, premoveEnabledClickAnywhereToCancel, thisPlayerUsesChessComputerAssistance, opening, takeback, proposeATakeback, takebackPropositionSent, takebackPropositionDeclined, takebackPropositionAccepted, takebackPropositionCanceled, yourOpponentProposesATakeback, bookmarkThisGame, toggleBackground, freeOnlineChessGamePlayChessNowInACleanInterfaceNoRegistrationNoAdsNoPluginRequiredPlayChessWithComputerFriendsOrRandomOpponents)
+ def keys = List(playWithAFriend, inviteAFriendToPlayWithYou, playWithTheMachine, challengeTheArtificialIntelligence, toInviteSomeoneToPlayGiveThisUrl, gameOver, waitingForOpponent, waiting, yourTurn, aiNameLevelAiLevel, level, toggleTheChat, toggleSound, chat, resign, checkmate, stalemate, white, black, createAGame, noGameAvailableRightNowCreateOne, whiteIsVictorious, blackIsVictorious, playWithTheSameOpponentAgain, newOpponent, playWithAnotherOpponent, yourOpponentWantsToPlayANewGameWithYou, joinTheGame, whitePlays, blackPlays, theOtherPlayerHasLeftTheGameYouCanForceResignationOrWaitForHim, makeYourOpponentResign, forceResignation, talkInChat, theFirstPersonToComeOnThisUrlWillPlayWithYou, whiteCreatesTheGame, blackCreatesTheGame, whiteJoinsTheGame, blackJoinsTheGame, whiteResigned, blackResigned, whiteLeftTheGame, blackLeftTheGame, shareThisUrlToLetSpectatorsSeeTheGame, youAreViewingThisGameAsASpectator, replayAndAnalyse, viewGameStats, flipBoard, threefoldRepetition, claimADraw, offerDraw, draw, nbConnectedPlayers, talkAboutChessAndDiscussLichessFeaturesInTheForum, seeTheGamesBeingPlayedInRealTime, gamesBeingPlayedRightNow, viewAllNbGames, viewNbCheckmates, nbBookmarks, nbPopularGames, nbAnalysedGames, bookmarkedByNbPlayers, viewInFullSize, logOut, signIn, signUp, people, games, forum, chessPlayers, minutesPerSide, variant, timeControl, start, username, password, haveAnAccount, allYouNeedIsAUsernameAndAPassword, learnMoreAboutLichess, rank, gamesPlayed, declineInvitation, cancel, timeOut, drawOfferSent, drawOfferDeclined, drawOfferAccepted, drawOfferCanceled, yourOpponentOffersADraw, accept, decline, playingRightNow, abortGame, gameAborted, standard, unlimited, mode, casual, rated, thisGameIsRated, rematch, rematchOfferSent, rematchOfferAccepted, rematchOfferCanceled, rematchOfferDeclined, cancelRematchOffer, viewRematch, play, inbox, chatRoom, spectatorRoom, composeMessage, sentMessages, incrementInSeconds, freeOnlineChess, spectators, nbWins, nbLosses, nbDraws, exportGames, color, eloRange, giveNbSeconds, searchAPlayer, whoIsOnline, allPlayers, namedPlayers, premoveEnabledClickAnywhereToCancel, thisPlayerUsesChessComputerAssistance, opening, takeback, proposeATakeback, takebackPropositionSent, takebackPropositionDeclined, takebackPropositionAccepted, takebackPropositionCanceled, yourOpponentProposesATakeback, bookmarkThisGame, toggleBackground, freeOnlineChessGamePlayChessNowInACleanInterfaceNoRegistrationNoAdsNoPluginRequiredPlayChessWithComputerFriendsOrRandomOpponents)
}
diff --git a/app/timeline/Entry.scala b/app/timeline/Entry.scala
index d2521c3503..ce41f913a8 100644
--- a/app/timeline/Entry.scala
+++ b/app/timeline/Entry.scala
@@ -2,7 +2,6 @@ package lila
package timeline
import com.novus.salat.annotations._
-import com.mongodb.BasicDBList
case class Entry(
gameId: String,
diff --git a/app/views/game/analysed.scala.html b/app/views/game/analysed.scala.html
new file mode 100644
index 0000000000..e33c54935f
--- /dev/null
+++ b/app/views/game/analysed.scala.html
@@ -0,0 +1,7 @@
+@(paginator: Paginator[DbGame], listMenu: lila.game.ListMenu)(implicit ctx: Context)
+
+@game.list(
+name = trans.nbAnalysedGames.str(listMenu.nbAnalysed.localize),
+paginator = paginator,
+next = paginator.nextPage map { n => routes.Game.analysed(n) },
+menu = sideMenu(listMenu, "analysed"))
diff --git a/app/views/game/sideMenu.scala.html b/app/views/game/sideMenu.scala.html
index 0ea6ee8208..f756af61dd 100644
--- a/app/views/game/sideMenu.scala.html
+++ b/app/views/game/sideMenu.scala.html
@@ -17,3 +17,6 @@
@trans.nbPopularGames(listMenu.nbPopular.localize)
+
+ @trans.nbAnalysedGames(listMenu.nbAnalysed.localize)
+
diff --git a/conf/application.conf b/conf/application.conf
index a6b85886dd..7fb21bdbc0 100644
--- a/conf/application.conf
+++ b/conf/application.conf
@@ -50,11 +50,14 @@ game {
message.lifetime = 30 seconds
uid.timeout = 10 seconds
hub.timeout = 2 minutes
- player.timeout = 1 minute
+ player.timeout = 1 minutes
animation.delay = 200 ms
- cached.nb.ttl = 10 minute
+ cached.nb.ttl = 10 minutes
paginator.max_per_page = 10
}
+analyse {
+ cached.nb.ttl = 10 minutes
+}
forum {
topic.max_per_page = 10
post.max_per_page = 10
@@ -136,7 +139,7 @@ logger {
akka {
loglevel = INFO
- stdout-loglevel = DEBUG
+ stdout-loglevel = INFO
log-config-on-start = off
event-handlers = ["lila.core.AkkaLogger"]
}
diff --git a/conf/messages b/conf/messages
index bda5203a4e..0f4833510e 100644
--- a/conf/messages
+++ b/conf/messages
@@ -58,6 +58,7 @@ viewAllNbGames=%s Games
viewNbCheckmates=%s Checkmates
nbBookmarks=%s Bookmarks
nbPopularGames=%s Popular Games
+nbAnalysedGames=%s Analysed Games
bookmarkedByNbPlayers=Bookmarked by %s players
viewInFullSize=View in full size
logOut=Log out
diff --git a/conf/routes b/conf/routes
index 62f5cdb93d..8321b9b0ce 100644
--- a/conf/routes
+++ b/conf/routes
@@ -7,6 +7,7 @@ GET /games/all controllers.Game.all(page: Int ?= 1)
GET /games/checkmate controllers.Game.checkmate(page: Int ?= 1)
GET /games/bookmark controllers.Game.bookmark(page: Int ?= 1)
GET /games/popular controllers.Game.popular(page: Int ?= 1)
+GET /games/analysed controllers.Game.analysed(page: Int ?= 1)
GET /games/featured/js controllers.Game.featuredJs(id: String ?= "")
# Round
diff --git a/todo b/todo
index 54f3ec7dfb..9e80713f95 100644
--- a/todo
+++ b/todo
@@ -30,3 +30,8 @@ legend feedback: http://en.lichess.org/forum/lichess-feedback/a-few-points-bugss
http://en.lichess.org/forum/lichess-feedback/problems-with-yin-yang#1
also translate websockets error message
play with a friend possible bug http://en.lichess.org/forum/lichess-feedback/play-with-a-friend-bug
+
+next deploy
+-----------
+db.analysis.ensureIndex({done:1})
+db.analysis.ensureIndex({date:-1})