From 8657e3497cdcc237cc180ba35dd66443b0bee755 Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Mon, 20 Jun 2016 09:49:38 +0200 Subject: [PATCH] complete invitation to limited tournametns --- app/controllers/Tournament.scala | 7 ++- modules/common/src/main/Future.scala | 10 +++- modules/tournament/src/main/Condition.scala | 2 + modules/tournament/src/main/Env.scala | 2 +- ...Notifier.scala => TournamentInviter.scala} | 47 +++++++++++-------- .../tournament/src/main/TournamentRepo.scala | 2 +- modules/tournament/src/main/model.scala | 9 ++-- ui/notify/src/view.js | 5 +- 8 files changed, 55 insertions(+), 29 deletions(-) rename modules/tournament/src/main/{TournamentNotifier.scala => TournamentInviter.scala} (54%) diff --git a/app/controllers/Tournament.scala b/app/controllers/Tournament.scala index 959e9fda16..eb70fb02e5 100644 --- a/app/controllers/Tournament.scala +++ b/app/controllers/Tournament.scala @@ -177,7 +177,12 @@ object Tournament extends LilaController { def limitedInvitation = Auth { implicit ctx => me => - ??? + env.api.fetchVisibleTournaments.flatMap { tours => + lila.tournament.TournamentInviter.findNextFor(me, tours, env.verify.canEnter(me)) + } map { + case None => Redirect(routes.Tournament.home(1)) + case Some(t) => Redirect(routes.Tournament.show(t.id)) + } } def websocket(id: String, apiVersion: Int) = SocketOption[JsValue] { implicit ctx => diff --git a/modules/common/src/main/Future.scala b/modules/common/src/main/Future.scala index f3bdb5a849..37ba4d8305 100644 --- a/modules/common/src/main/Future.scala +++ b/modules/common/src/main/Future.scala @@ -32,6 +32,14 @@ object Future { def applySequentially[A](list: List[A])(f: A => Funit): Funit = list match { case h :: t => f(h) >> applySequentially(t)(f) - case Nil => funit + case Nil => funit } + + def find[A](list: List[A])(f: A => Fu[Boolean]): Fu[Option[A]] = list match { + case Nil => fuccess(none) + case h :: t => f(h).flatMap { + case true => fuccess(h.some) + case false => find(t)(f) + } + } } diff --git a/modules/tournament/src/main/Condition.scala b/modules/tournament/src/main/Condition.scala index 93aaa4b76d..dc14ca1abc 100644 --- a/modules/tournament/src/main/Condition.scala +++ b/modules/tournament/src/main/Condition.scala @@ -88,6 +88,8 @@ object Condition { val getMaxRating: GetMaxRating = perf => historyApi.lastMonthTopRating(user, perf) all.withVerdicts(getMaxRating)(user) } + def canEnter(user: User)(tour: Tournament): Fu[Boolean] = + apply(tour.conditions, user).map(_.accepted) } object BSONHandlers { diff --git a/modules/tournament/src/main/Env.scala b/modules/tournament/src/main/Env.scala index 9eed3c97ea..69b7a2736d 100644 --- a/modules/tournament/src/main/Env.scala +++ b/modules/tournament/src/main/Env.scala @@ -141,7 +141,7 @@ final class Env( TournamentScheduler.start(system, api) - // TournamentNotifier.start(system, api, notifyApi) + TournamentInviter.start(system, api, notifyApi) def version(tourId: String): Fu[Int] = socketHub ? Ask(tourId, GetVersion) mapTo manifest[Int] diff --git a/modules/tournament/src/main/TournamentNotifier.scala b/modules/tournament/src/main/TournamentInviter.scala similarity index 54% rename from modules/tournament/src/main/TournamentNotifier.scala rename to modules/tournament/src/main/TournamentInviter.scala index 568ed81d5b..4512c17025 100644 --- a/modules/tournament/src/main/TournamentNotifier.scala +++ b/modules/tournament/src/main/TournamentInviter.scala @@ -10,15 +10,11 @@ import lila.db.dsl._ import lila.notify.{ Notification, NotifyApi, LimitedTournamentInvitation } import lila.rating.PerfType -private final class TournamentNotifier private ( +private final class TournamentInviter private ( api: TournamentApi, notifyApi: NotifyApi) extends Actor { - import TournamentNotifier._ - - val minGames = 20 - val maxRating = 1700 - val perfs = List(PerfType.Blitz, PerfType.Classical) + import TournamentInviter._ def receive = { @@ -31,18 +27,13 @@ private final class TournamentNotifier private ( } } - def qualifies(user: User) = { - println(user.seenRecently, user.id) - println { - !user.seenRecently && - !user.kid && - user.count.rated > 50 && - user.toints < 100 && - user.perfs.bestRatingInWithMinGames(perfs, minGames).??(maxRating >=) && - !alreadyNotified(user) - } - !user.seenRecently - } + def qualifies(user: User) = + !user.seenRecently && + !user.kid && + user.count.rated > 50 && + user.toints < 10 && + bestRating(user).??(1700 >=) && + !alreadyNotified(user) def alreadyNotified(user: User) = if (notifiedCache get user.id) false @@ -54,10 +45,26 @@ private final class TournamentNotifier private ( val notifiedCache = new lila.memo.ExpireSetMemo(1 hour) } -private object TournamentNotifier { +object TournamentInviter { + + val minGames = 20 + val perfs = List(PerfType.Blitz, PerfType.Classical) + + def bestRating(user: User) = user.perfs.bestRatingInWithMinGames(perfs, minGames) + + def findNextFor( + user: User, + tours: VisibleTournaments, + canEnter: Tournament => Fu[Boolean]): Fu[Option[Tournament]] = bestRating(user) match { + case None => fuccess(none) + case Some(rating) if rating > 2000 => fuccess(none) + case Some(rating) => lila.common.Future.find(tours.unfinished.filter { t => + t.conditions.maxRating.??(_.rating >= rating) + }.take(4))(canEnter) + } def start(system: ActorSystem, api: TournamentApi, notifyApi: NotifyApi) = { - val ref = system.actorOf(Props(new TournamentNotifier(api, notifyApi))) + val ref = system.actorOf(Props(new TournamentInviter(api, notifyApi))) system.lilaBus.subscribe(ref, 'userActive) } } diff --git a/modules/tournament/src/main/TournamentRepo.scala b/modules/tournament/src/main/TournamentRepo.scala index b7a219207f..cd7558c41f 100644 --- a/modules/tournament/src/main/TournamentRepo.scala +++ b/modules/tournament/src/main/TournamentRepo.scala @@ -161,7 +161,7 @@ object TournamentRepo { } getOrElse 30 } - def promotable: Fu[List[Tournament]] = + private[tournament] def promotable: Fu[List[Tournament]] = stillWorthEntering zip publicCreatedSorted(24 * 60) map { case (started, created) => (started ::: created).foldLeft(List.empty[Tournament]) { case (acc, tour) if !isPromotable(tour) => acc diff --git a/modules/tournament/src/main/model.scala b/modules/tournament/src/main/model.scala index 14d3187b0f..9a4cb92d17 100644 --- a/modules/tournament/src/main/model.scala +++ b/modules/tournament/src/main/model.scala @@ -11,9 +11,12 @@ case class PlayerInfo(rank: Int, withdraw: Boolean) { } case class VisibleTournaments( - created: List[Tournament], - started: List[Tournament], - finished: List[Tournament]) + created: List[Tournament], + started: List[Tournament], + finished: List[Tournament]) { + + def unfinished = created ::: started +} case class PlayerInfoExt( tour: Tournament, diff --git a/ui/notify/src/view.js b/ui/notify/src/view.js index a3029590bf..a01d08f150 100644 --- a/ui/notify/src/view.js +++ b/ui/notify/src/view.js @@ -1,6 +1,7 @@ var m = require('mithril'); function userFullName(u) { + if (!u) return '?'; return u.title ? u.title + ' ' + u.name : u.name; } @@ -135,10 +136,10 @@ var handlers = { return genericNotification(notification, url, 'g', [ m('span', [ - m('strong', 'New rating limited tournaments!'), + m('strong', 'Low rated tournament'), drawTime(notification) ]), - m('span', 'Experience lichess arena tournaments with players of your level') + m('span', 'A tournament you can win!') ]); }, text: function(n) {