diff --git a/app/mashup/UserInfo.scala b/app/mashup/UserInfo.scala index 25fbb5b1e5..ac5442dd85 100644 --- a/app/mashup/UserInfo.scala +++ b/app/mashup/UserInfo.scala @@ -12,7 +12,7 @@ case class UserInfo( user: User, rank: Option[(Int, Int)], nbPlaying: Int, - nbWithMe: Option[Int], + confrontation: Option[(Int, Int, Int)], nbBookmark: Int, eloWithMe: Option[List[(String, Int)]], eloChart: Option[EloChart], @@ -22,6 +22,10 @@ case class UserInfo( def nbRated = user.count.rated + def nbWithMe = confrontation map { + case (w, d, l) ⇒ w + d + l + } + def percentRated: Int = math.round(nbRated / user.count.game.toFloat * 100) } @@ -41,18 +45,18 @@ object UserInfo { GameRepo count (_ notFinished user.id) map (_.some) }) zip (ctx.me.filter(user!=) ?? { me ⇒ - GameRepo count (_.opponents(user, me)) map (_.some) + GameRepo.confrontation(user, me) map (_.some) }) zip (bookmarkApi countByUser user) zip EloChart(user) zip relationApi.nbFollowing(user.id) zip relationApi.nbFollowers(user.id) zip postApi.nbByUser(user.id) map { - case (((((((rank, nbPlaying), nbWithMe), nbBookmark), eloChart), nbFollowing), nbFollowers), nbPosts) ⇒ new UserInfo( + case (((((((rank, nbPlaying), confrontation), nbBookmark), eloChart), nbFollowing), nbFollowers), nbPosts) ⇒ new UserInfo( user = user, rank = rank, nbPlaying = ~nbPlaying, - nbWithMe = nbWithMe, + confrontation = confrontation, nbBookmark = nbBookmark, eloWithMe = ctx.me.filter(user !=) map { me ⇒ List( diff --git a/app/views/user/show.scala.html b/app/views/user/show.scala.html index 4f1ab5c8ea..67e02a28f0 100644 --- a/app/views/user/show.scala.html +++ b/app/views/user/show.scala.html @@ -111,12 +111,23 @@ evenMoreCss = evenMoreCss) { } @bio @info.eloWithMe.map { eloWithMe => -
+
+ @trans.ratedGameResult()
@eloWithMe.map { e => @e._1.capitalize: @showNumber(e._2) }
} + @info.confrontation.map { + case (w, d, l) => { +
+ @ctx.me.map(_.username) vs @u.username
+ @trans.nbWins(""+w+""), + @trans.nbDraws(""+d+""), + @trans.nbLosses(""+l+"") +
+ } + }

@trans.memberSince() @showDate(u.createdAt)

@u.seenAt.map { seen => diff --git a/conf/messages b/conf/messages index 10146b6be1..e3d5530740 100644 --- a/conf/messages +++ b/conf/messages @@ -244,3 +244,4 @@ boardEditor=Board editor startPosition=Start position clearBoard=Clear board savePosition=Save position +ratedGameResult=Rated game result diff --git a/modules/game/src/main/ComputeElos.scala b/modules/game/src/main/ComputeElos.scala index 23d6efd987..a47a9e46eb 100644 --- a/modules/game/src/main/ComputeElos.scala +++ b/modules/game/src/main/ComputeElos.scala @@ -68,8 +68,7 @@ private[game] final class ComputeElos(system: ActorSystem) { private def usersQuery = $query.apply[User]( Json.obj( - "count.rated" -> $gt(0), - "_id" -> "legend" + "count.rated" -> $gt(0) )) sort ($sort desc "seenAt") private def gamesQuery(user: User) = $query.apply[Game]( diff --git a/modules/game/src/main/GameRepo.scala b/modules/game/src/main/GameRepo.scala index 30d8be2a2e..794eeb3828 100644 --- a/modules/game/src/main/GameRepo.scala +++ b/modules/game/src/main/GameRepo.scala @@ -2,18 +2,17 @@ package lila.game import scala.util.Random -import org.joda.time.DateTime -import org.scala_tools.time.Imports._ -import play.api.libs.json._ -import play.modules.reactivemongo.json.BSONFormats.toJSON -import play.modules.reactivemongo.json.ImplicitBSONHandlers.JsObjectWriter - import chess.format.Forsyth import chess.{ Color, Variant, Status } import lila.common.PimpedJson._ import lila.db.api._ import lila.db.Implicits._ import lila.user.User +import org.joda.time.DateTime +import org.scala_tools.time.Imports._ +import play.api.libs.json._ +import play.modules.reactivemongo.json.BSONFormats.toJSON +import play.modules.reactivemongo.json.ImplicitBSONHandlers.JsObjectWriter import tube.gameTube object GameRepo { @@ -197,9 +196,9 @@ object GameRepo { import reactivemongo.core.commands._ val command = Aggregate(gameTube.coll.name, Seq( Match(BSONDocument("uids" -> userId)), - Match(BSONDocument("uids" -> BSONDocument("$size" -> BSONInteger(2)))), + Match(BSONDocument("uids" -> BSONDocument("$size" -> 2))), Unwind("uids"), - Match(BSONDocument("uids" -> BSONDocument("$ne" -> BSONString(userId)))), + Match(BSONDocument("uids" -> BSONDocument("$ne" -> userId))), GroupField("uids")("gs" -> SumValue(1)), Sort(Seq(Descending("gs"))), Limit(limit) @@ -214,4 +213,32 @@ object GameRepo { }).flatten } } + + // user1 wins, draws, losses + def confrontation(user1: User, user2: User): Fu[(Int, Int, Int)] = { + import reactivemongo.bson._ + import reactivemongo.core.commands._ + val userIds = List(user1, user2).sortBy(_.count.game).map(_.id) + val command = Aggregate(gameTube.coll.name, Seq( + Match(BSONDocument( + "uids" -> BSONDocument("$all" -> userIds), + "s" -> BSONDocument("$gte" -> chess.Status.Mate.id) + )), + GroupField("wid")("nb" -> SumValue(1)) + )) + gameTube.coll.db.command(command) map { stream ⇒ + val res = (stream.toList map { obj ⇒ + toJSON(obj).asOpt[JsObject] flatMap { o ⇒ + o int "nb" map { nb ⇒ + ~(o str "_id") -> nb + } + } + }).flatten.toMap + ( + ~(res get user1.id), + ~(res get ""), + ~(res get user2.id) + ) + } + } } diff --git a/modules/i18n/src/main/I18nKeys.scala b/modules/i18n/src/main/I18nKeys.scala index 659ff3c1a6..40dba0cd59 100644 --- a/modules/i18n/src/main/I18nKeys.scala +++ b/modules/i18n/src/main/I18nKeys.scala @@ -268,6 +268,7 @@ final class I18nKeys(translator: Translator) { val startPosition = new Key("startPosition") val clearBoard = new Key("clearBoard") val savePosition = new Key("savePosition") + val ratedGameResult = new Key("ratedGameResult") - 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, computerAnalysisInProgress, theComputerAnalysisYouRequestedIsNowAvailable, theComputerAnalysisHasFailed, viewTheComputerAnalysis, requestAComputerAnalysis, blunders, mistakes, inaccuracies, viewGameStats, flipBoard, threefoldRepetition, claimADraw, offerDraw, draw, nbConnectedPlayers, talkAboutChessAndDiscussLichessFeaturesInTheForum, seeTheGamesBeingPlayedInRealTime, gamesBeingPlayedRightNow, viewAllNbGames, viewNbCheckmates, nbBookmarks, nbPopularGames, nbAnalysedGames, bookmarkedByNbPlayers, viewInFullSize, logOut, signIn, newToLichess, youNeedAnAccountToDoThat, signUp, people, games, forum, xPostedInForumY, chessPlayers, minutesPerSide, variant, timeControl, time, start, username, password, haveAnAccount, allYouNeedIsAUsernameAndAPassword, learnMoreAboutLichess, rank, gamesPlayed, nbGamesWithYou, declineInvitation, cancel, timeOut, drawOfferSent, drawOfferDeclined, drawOfferAccepted, drawOfferCanceled, yourOpponentOffersADraw, accept, decline, playingRightNow, finished, abortGame, gameAborted, standard, unlimited, mode, casual, rated, thisGameIsRated, rematch, rematchOfferSent, rematchOfferAccepted, rematchOfferCanceled, rematchOfferDeclined, cancelRematchOffer, viewRematch, play, inbox, chatRoom, spectatorRoom, composeMessage, sentMessages, noNewMessages, subject, recipient, send, incrementInSeconds, freeOnlineChess, spectators, nbWins, nbLosses, nbDraws, exportGames, color, eloRange, giveNbSeconds, whoIsOnline, allPlayers, premoveEnabledClickAnywhereToCancel, thisPlayerUsesChessComputerAssistance, opening, takeback, proposeATakeback, takebackPropositionSent, takebackPropositionDeclined, takebackPropositionAccepted, takebackPropositionCanceled, yourOpponentProposesATakeback, bookmarkThisGame, toggleBackground, search, advancedSearch, tournament, tournaments, tournamentPoints, viewTournament, freeOnlineChessGamePlayChessNowInACleanInterfaceNoRegistrationNoAdsNoPluginRequiredPlayChessWithComputerFriendsOrRandomOpponents, teams, nbMembers, allTeams, newTeam, myTeams, noTeamFound, joinTeam, quitTeam, anyoneCanJoin, aConfirmationIsRequiredToJoin, joiningPolicy, teamLeader, teamBestPlayers, teamRecentMembers, xJoinedTeamY, xCreatedTeamY, averageElo, location, settings, filterGames, reset, apply, leaderboard, pasteTheFenStringHere, pasteThePgnStringHere, fromPosition, continueFromHere, importGame, nbImportedGames, thisIsAChessCaptcha, clickOnTheBoardToMakeYourMove, notACheckmate, colorPlaysCheckmateInOne, retry, reconnecting, onlineFriends, noFriendsOnline, findFriends, favoriteOpponents, follow, following, unfollow, block, blocked, unblock, followsYou, xStartedFollowingY, nbFollowers, nbFollowing, profile, more, memberSince, lastLogin, challengeToPlay, player, list, graph, all, lessThanNbMinutes, xToYMinutes, textIsTooShort, textIsTooLong, required, addToChrome, openTournaments, duration, winner, standing, createANewTournament, join, withdraw, points, wins, losses, winStreak, createdBy, waitingForNbPlayers, tournamentIsStarting, nbMinutesPerSidePlusNbSecondsPerMove, anonymousCanJoin, boardEditor, startPosition, clearBoard, savePosition) + 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, computerAnalysisInProgress, theComputerAnalysisYouRequestedIsNowAvailable, theComputerAnalysisHasFailed, viewTheComputerAnalysis, requestAComputerAnalysis, blunders, mistakes, inaccuracies, viewGameStats, flipBoard, threefoldRepetition, claimADraw, offerDraw, draw, nbConnectedPlayers, talkAboutChessAndDiscussLichessFeaturesInTheForum, seeTheGamesBeingPlayedInRealTime, gamesBeingPlayedRightNow, viewAllNbGames, viewNbCheckmates, nbBookmarks, nbPopularGames, nbAnalysedGames, bookmarkedByNbPlayers, viewInFullSize, logOut, signIn, newToLichess, youNeedAnAccountToDoThat, signUp, people, games, forum, xPostedInForumY, chessPlayers, minutesPerSide, variant, timeControl, time, start, username, password, haveAnAccount, allYouNeedIsAUsernameAndAPassword, learnMoreAboutLichess, rank, gamesPlayed, nbGamesWithYou, declineInvitation, cancel, timeOut, drawOfferSent, drawOfferDeclined, drawOfferAccepted, drawOfferCanceled, yourOpponentOffersADraw, accept, decline, playingRightNow, finished, abortGame, gameAborted, standard, unlimited, mode, casual, rated, thisGameIsRated, rematch, rematchOfferSent, rematchOfferAccepted, rematchOfferCanceled, rematchOfferDeclined, cancelRematchOffer, viewRematch, play, inbox, chatRoom, spectatorRoom, composeMessage, sentMessages, noNewMessages, subject, recipient, send, incrementInSeconds, freeOnlineChess, spectators, nbWins, nbLosses, nbDraws, exportGames, color, eloRange, giveNbSeconds, whoIsOnline, allPlayers, premoveEnabledClickAnywhereToCancel, thisPlayerUsesChessComputerAssistance, opening, takeback, proposeATakeback, takebackPropositionSent, takebackPropositionDeclined, takebackPropositionAccepted, takebackPropositionCanceled, yourOpponentProposesATakeback, bookmarkThisGame, toggleBackground, search, advancedSearch, tournament, tournaments, tournamentPoints, viewTournament, freeOnlineChessGamePlayChessNowInACleanInterfaceNoRegistrationNoAdsNoPluginRequiredPlayChessWithComputerFriendsOrRandomOpponents, teams, nbMembers, allTeams, newTeam, myTeams, noTeamFound, joinTeam, quitTeam, anyoneCanJoin, aConfirmationIsRequiredToJoin, joiningPolicy, teamLeader, teamBestPlayers, teamRecentMembers, xJoinedTeamY, xCreatedTeamY, averageElo, location, settings, filterGames, reset, apply, leaderboard, pasteTheFenStringHere, pasteThePgnStringHere, fromPosition, continueFromHere, importGame, nbImportedGames, thisIsAChessCaptcha, clickOnTheBoardToMakeYourMove, notACheckmate, colorPlaysCheckmateInOne, retry, reconnecting, onlineFriends, noFriendsOnline, findFriends, favoriteOpponents, follow, following, unfollow, block, blocked, unblock, followsYou, xStartedFollowingY, nbFollowers, nbFollowing, profile, more, memberSince, lastLogin, challengeToPlay, player, list, graph, all, lessThanNbMinutes, xToYMinutes, textIsTooShort, textIsTooLong, required, addToChrome, openTournaments, duration, winner, standing, createANewTournament, join, withdraw, points, wins, losses, winStreak, createdBy, waitingForNbPlayers, tournamentIsStarting, nbMinutesPerSidePlusNbSecondsPerMove, anonymousCanJoin, boardEditor, startPosition, clearBoard, savePosition, ratedGameResult) } diff --git a/public/stylesheets/dark.css b/public/stylesheets/dark.css index 23e8674813..30f022525b 100644 --- a/public/stylesheets/dark.css +++ b/public/stylesheets/dark.css @@ -73,7 +73,7 @@ body.dark #site_header div.side_menu a.active, body.dark div.game_row, body.dark div.user_lists form.search_user_form input, body.dark div.user_lists div.all_users, -body.dark div.user_show div.elo_with_me, +body.dark div.user_show div.boxed_data, body.dark #lichess_forum div.post, body.dark #lichess_forum textarea, body.dark #lichess_forum form.wide input, @@ -251,7 +251,7 @@ body.dark .mini_board { body.dark div.game_row:nth-child(odd), body.dark #lichess_forum table.forum_table tr:nth-child(odd), body.dark #lichess_message tr:nth-child(even), -body.dark div.user_show .elo_with_me, +body.dark div.user_show div.boxed_data, body.dark div.content_box_inter, body.dark #GameText tr:nth-child(even), body.dark table.slist tbody tr:nth-child(even), diff --git a/public/stylesheets/user-show.css b/public/stylesheets/user-show.css index 70c7959adc..b51d179ca5 100644 --- a/public/stylesheets/user-show.css +++ b/public/stylesheets/user-show.css @@ -76,7 +76,7 @@ div.user_show .user-infos { } div.user_show .user_bio, -div.user_show .elo_with_me, +div.user_show .boxed_data, div.user_show .stats, div.user_show .tournament_points, div.user_show .teams { @@ -91,7 +91,7 @@ div.user_show .teams a { margin: 5px 0; } -div.user_show div.elo_with_me { +div.user_show div.boxed_data { width: 292px; padding: 5px 8px; border: 1px solid #eaeaea; diff --git a/todo b/todo index eb5f4ae825..d0e7d5563f 100644 --- a/todo +++ b/todo @@ -71,6 +71,7 @@ opera bug http://postimg.org/image/zcv8hse8n/full/ customize sound notifications http://imgur.com/70WVyb5 show friend game results on timeline opera issue http://en.lichess.org/forum/lichess-feedback/new-game-wont-show-on-games-list-opera#1 +analyse must update search index ---