implement new ranking API

pull/1634/head
Thibault Duplessis 2016-02-21 12:03:26 +07:00
parent 6998a33f4c
commit 5ae3c70663
5 changed files with 73 additions and 4 deletions

View File

@ -538,6 +538,7 @@ user {
user = user4
note = note
trophy = trophy
ranking = ranking
}
}
history {

View File

@ -22,6 +22,7 @@ final class Env(
playban: lila.playban.PlaybanApi,
lightUser: String => Option[lila.common.LightUser],
userJsonView: lila.user.JsonView,
rankingApi: lila.user.RankingApi,
uciMemo: lila.game.UciMemo,
rematch960Cache: lila.memo.ExpireSetMemo,
isRematchCache: lila.memo.ExpireSetMemo,
@ -112,7 +113,7 @@ final class Env(
messenger = messenger,
bus = system.lilaBus)
lazy val perfsUpdater = new PerfsUpdater(historyApi)
lazy val perfsUpdater = new PerfsUpdater(historyApi, rankingApi)
lazy val forecastApi: ForecastApi = new ForecastApi(
coll = db(CollectionForecast),
@ -214,6 +215,7 @@ object Env {
playban = lila.playban.Env.current.api,
lightUser = lila.user.Env.current.lightUser,
userJsonView = lila.user.Env.current.jsonView,
rankingApi = lila.user.Env.current.rankingApi,
uciMemo = lila.game.Env.current.uciMemo,
rematch960Cache = lila.game.Env.current.cached.rematch960,
isRematchCache = lila.game.Env.current.cached.isRematch,

View File

@ -8,9 +8,11 @@ import play.api.Logger
import lila.game.{ GameRepo, Game, Pov, PerfPicker }
import lila.history.HistoryApi
import lila.rating.{ Glicko, Perf, PerfType => PT }
import lila.user.{ UserRepo, User, Perfs }
import lila.user.{ UserRepo, User, Perfs, RankingApi }
final class PerfsUpdater(historyApi: HistoryApi) {
final class PerfsUpdater(
historyApi: HistoryApi,
rankingApi: RankingApi) {
private val VOLATILITY = Glicko.default.volatility
private val TAU = 0.75d
@ -65,7 +67,9 @@ final class PerfsUpdater(historyApi: HistoryApi) {
UserRepo.setPerfs(white, perfsW, white.perfs) zip
UserRepo.setPerfs(black, perfsB, black.perfs) zip
historyApi.add(white, game, perfsW) zip
historyApi.add(black, game, perfsB)
historyApi.add(black, game, perfsB) zip
rankingApi.save(white.id, game.perfType, perfsW) zip
rankingApi.save(black.id, game.perfType, perfsB)
}.void
}

View File

@ -21,6 +21,7 @@ final class Env(
val CollectionUser = config getString "collection.user"
val CollectionNote = config getString "collection.note"
val CollectionTrophy = config getString "collection.trophy"
val CollectionRanking = config getString "collection.ranking"
}
import settings._
@ -34,6 +35,8 @@ final class Env(
lazy val trophyApi = new TrophyApi(db(CollectionTrophy))
lazy val rankingApi = new RankingApi(db(CollectionRanking))
lazy val jsonView = new JsonView(isOnline)
val forms = DataForm

View File

@ -0,0 +1,59 @@
package lila.user
import org.joda.time.DateTime
import play.api.libs.iteratee._
import reactivemongo.bson._
import scala.concurrent.duration._
import lila.db.BSON.BSONJodaDateTimeHandler
import lila.db.BSON.MapValue.MapHandler
import lila.memo.AsyncCache
import lila.rating.{ Perf, PerfType }
final class RankingApi(coll: lila.db.Types.Coll) {
private type Rating = Int
def save(userId: User.ID, perfType: Option[PerfType], perfs: Perfs): Funit =
perfType ?? { pt =>
save(userId, pt, perfs(pt).intRating)
}
def save(userId: User.ID, perfType: PerfType, rating: Rating): Funit =
coll.update(BSONDocument(
"_id" -> makeId(userId, perfType)
), BSONDocument(
"user" -> userId,
"perf" -> perfType.key,
"rating" -> rating,
"expiresAt" -> DateTime.now.plusDays(7)),
upsert = true).void
def getAll(userId: User.ID): Fu[Map[Perf.Key, Int]] =
lila.common.Future.traverseSequentially(PerfType.leaderboardable) { perf =>
cache(perf.key) map { _ get userId map (perf.key -> _) }
} map (_.flatten.toMap)
private val cache = AsyncCache[Perf.Key, Map[User.ID, Rating]](
f = compute,
timeToLive = 15 minutes)
private def compute(perfKey: Perf.Key): Fu[Map[User.ID, Rating]] = {
val enumerator = coll.find(
BSONDocument("perf" -> perfKey),
BSONDocument("user" -> true, "_id" -> false)
).sort(BSONDocument("rating" -> -1)).cursor[BSONDocument]().enumerate()
var rank = 1
val b = Map.newBuilder[User.ID, Rating]
val mapBuilder: Iteratee[BSONDocument, Unit] = Iteratee.foreach { doc =>
doc.getAs[User.ID]("user") foreach { user =>
b += (user -> rank)
rank = rank + 1
}
}
enumerator.run(mapBuilder) inject b.result
}
private def makeId(user: User.ID, perfType: PerfType) =
s"$user:${perfType.key}"
}