add swiss tournament result to user activity feed - closes #7708
parent
9ea1775ddc
commit
dc7c49f7b4
|
@ -8,6 +8,7 @@ import lila.api.Context
|
|||
import lila.app.templating.Environment._
|
||||
import lila.app.ui.ScalatagsTemplate._
|
||||
import lila.user.User
|
||||
import lila.swiss.Swiss
|
||||
|
||||
object activity {
|
||||
|
||||
|
@ -33,6 +34,7 @@ object activity {
|
|||
a.simuls map renderSimuls(u),
|
||||
a.studies map renderStudies,
|
||||
a.tours map renderTours,
|
||||
a.swisses map renderSwisses,
|
||||
a.teams map renderTeams,
|
||||
a.stream option renderStream(u),
|
||||
a.signup option renderSignup
|
||||
|
@ -245,7 +247,6 @@ object activity {
|
|||
trans.activity.competedInNbTournaments.pluralSame(tours.nb),
|
||||
subTag(
|
||||
tours.best.map { t =>
|
||||
val link = a(href := routes.Tournament.show(t.tourId))(tournamentIdToName(t.tourId))
|
||||
div(
|
||||
cls := List(
|
||||
"is-gold" -> (t.rank == 1),
|
||||
|
@ -258,7 +259,32 @@ object activity {
|
|||
strong(t.rank),
|
||||
t.rankRatio.percent,
|
||||
t.nbGames,
|
||||
link
|
||||
a(href := routes.Tournament.show(t.tourId))(tournamentIdToName(t.tourId))
|
||||
),
|
||||
br
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
private def renderSwisses(swisses: List[(Swiss.IdName, Int)])(implicit ctx: Context) =
|
||||
entryTag(
|
||||
iconTag("g"),
|
||||
div(
|
||||
trans.activity.competedInNbSwissTournaments.pluralSame(swisses.size),
|
||||
subTag(
|
||||
swisses.map { case (swiss, rank) =>
|
||||
div(
|
||||
cls := List(
|
||||
"is-gold" -> (rank == 1),
|
||||
"text" -> (rank <= 3)
|
||||
),
|
||||
dataIcon := (rank <= 3).option("g")
|
||||
)(
|
||||
trans.activity.rankedInSwissTournament(
|
||||
strong(rank),
|
||||
a(href := routes.Swiss.show(swiss.id.value))(swiss.name)
|
||||
),
|
||||
br
|
||||
)
|
||||
|
|
|
@ -215,7 +215,7 @@ lazy val pool = module("pool",
|
|||
)
|
||||
|
||||
lazy val activity = module("activity",
|
||||
Seq(common, game, analyse, user, forum, study, pool, puzzle, tournament, practice, team),
|
||||
Seq(common, game, analyse, user, forum, study, pool, puzzle, tournament, swiss, practice, team),
|
||||
reactivemongo.bundle
|
||||
)
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import org.joda.time.Interval
|
|||
|
||||
import lila.common.Day
|
||||
import lila.user.User
|
||||
import lila.swiss.Swiss
|
||||
|
||||
case class Activity(
|
||||
id: Activity.Id,
|
||||
|
@ -20,6 +21,7 @@ case class Activity(
|
|||
follows: Option[Follows] = None,
|
||||
studies: Option[Studies] = None,
|
||||
teams: Option[Teams] = None,
|
||||
swisses: Option[Swisses] = None,
|
||||
stream: Boolean = false
|
||||
) {
|
||||
|
||||
|
@ -40,7 +42,8 @@ case class Activity(
|
|||
patron,
|
||||
follows,
|
||||
studies,
|
||||
teams
|
||||
teams,
|
||||
swisses
|
||||
)
|
||||
.forall(_.isEmpty)
|
||||
}
|
||||
|
|
|
@ -17,7 +17,8 @@ final class ActivityReadApi(
|
|||
postApi: lila.forum.PostApi,
|
||||
simulApi: lila.simul.SimulApi,
|
||||
studyApi: lila.study.StudyApi,
|
||||
tourLeaderApi: lila.tournament.LeaderboardApi
|
||||
tourLeaderApi: lila.tournament.LeaderboardApi,
|
||||
swissApi: lila.swiss.SwissApi
|
||||
)(implicit ec: scala.concurrent.ExecutionContext) {
|
||||
|
||||
import BSONHandlers._
|
||||
|
@ -83,13 +84,13 @@ final class ActivityReadApi(
|
|||
.?? { simuls =>
|
||||
simulApi byIds simuls.value.map(_.value) dmap some
|
||||
}
|
||||
.map(_ filter (_.nonEmpty))
|
||||
.dmap(_.filter(_.nonEmpty))
|
||||
studies <-
|
||||
a.studies
|
||||
.?? { studies =>
|
||||
studyApi publicIdNames studies.value dmap some
|
||||
}
|
||||
.map(_ filter (_.nonEmpty))
|
||||
.dmap(_.filter(_.nonEmpty))
|
||||
tours <- a.games.exists(_.hasNonCorres) ?? {
|
||||
val dateRange = a.date -> a.date.plusDays(1)
|
||||
tourLeaderApi
|
||||
|
@ -106,6 +107,21 @@ final class ActivityReadApi(
|
|||
}
|
||||
.mon(_.user segment "activity.tours")
|
||||
}
|
||||
swisses <-
|
||||
a.swisses
|
||||
.?? { swisses =>
|
||||
swissApi
|
||||
.idNames(swisses.value.map(_.id))
|
||||
.map {
|
||||
_.flatMap { idName =>
|
||||
swisses.value.find(_.id == idName.id) map { s =>
|
||||
(idName, s.rank)
|
||||
}
|
||||
}
|
||||
}
|
||||
.dmap(_.some.filter(_.nonEmpty))
|
||||
}
|
||||
|
||||
} yield ActivityView(
|
||||
interval = a.interval,
|
||||
games = a.games,
|
||||
|
@ -121,6 +137,7 @@ final class ActivityReadApi(
|
|||
studies = studies,
|
||||
teams = a.teams,
|
||||
tours = tours,
|
||||
swisses = swisses,
|
||||
stream = a.stream
|
||||
)
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import lila.tournament.LeaderboardApi.{ Entry => TourEntry }
|
|||
|
||||
import activities._
|
||||
import model._
|
||||
import lila.swiss.Swiss
|
||||
|
||||
case class ActivityView(
|
||||
interval: Interval,
|
||||
|
@ -26,6 +27,7 @@ case class ActivityView(
|
|||
studies: Option[List[Study.IdName]] = None,
|
||||
teams: Option[Teams] = None,
|
||||
tours: Option[ActivityView.Tours] = None,
|
||||
swisses: Option[List[(Swiss.IdName, Int)]] = None,
|
||||
stream: Boolean = false,
|
||||
signup: Boolean = false
|
||||
)
|
||||
|
|
|
@ -150,6 +150,11 @@ final class ActivityWriteApi(
|
|||
def streamStart(userId: User.ID) =
|
||||
update(userId) { _.copy(stream = true).some }
|
||||
|
||||
def swiss(id: lila.swiss.Swiss.Id, ranking: lila.swiss.Ranking) =
|
||||
ranking.map { case (userId, rank) =>
|
||||
update(userId) { a => a.copy(swisses = Some(~a.swisses + SwissRank(id, rank))).some }
|
||||
}.sequenceFu
|
||||
|
||||
def erase(user: User) = coll.delete.one(regexId(user.id))
|
||||
|
||||
private def simulParticipant(simul: lila.simul.Simul, userId: String) =
|
||||
|
|
|
@ -7,8 +7,10 @@ import lila.common.{ Day, Iso }
|
|||
import lila.db.dsl._
|
||||
import lila.rating.BSONHandlers.perfTypeKeyIso
|
||||
import lila.rating.PerfType
|
||||
import lila.study.BSONHandlers._
|
||||
import lila.study.BSONHandlers.StudyIdBSONHandler
|
||||
import lila.study.Study
|
||||
import lila.swiss.BsonHandlers.swissIdHandler
|
||||
import lila.swiss.Swiss
|
||||
import lila.user.User
|
||||
|
||||
private object BSONHandlers {
|
||||
|
@ -117,6 +119,13 @@ private object BSONHandlers {
|
|||
implicit private lazy val teamsHandler =
|
||||
isoHandler[Teams, List[String]]((s: Teams) => s.value, Teams.apply _)
|
||||
|
||||
implicit lazy val swissRankHandler = new lila.db.BSON[SwissRank] {
|
||||
def reads(r: lila.db.BSON.Reader) = SwissRank(Swiss.Id(r.str("i")), r.intD("r"))
|
||||
def writes(w: lila.db.BSON.Writer, s: SwissRank) = BSONDocument("i" -> s.id, "r" -> s.rank)
|
||||
}
|
||||
implicit private lazy val swissesHandler =
|
||||
isoHandler[Swisses, List[SwissRank]]((s: Swisses) => s.value, Swisses.apply _)
|
||||
|
||||
object ActivityFields {
|
||||
val id = "_id"
|
||||
val games = "g"
|
||||
|
@ -131,6 +140,7 @@ private object BSONHandlers {
|
|||
val follows = "f"
|
||||
val studies = "t"
|
||||
val teams = "e"
|
||||
val swisses = "w"
|
||||
val stream = "st"
|
||||
}
|
||||
|
||||
|
@ -153,6 +163,7 @@ private object BSONHandlers {
|
|||
follows = r.getO[Follows](follows).filterNot(_.isEmpty),
|
||||
studies = r.getO[Studies](studies),
|
||||
teams = r.getO[Teams](teams),
|
||||
swisses = r.getO[Swisses](swisses),
|
||||
stream = r.getD[Boolean](stream)
|
||||
)
|
||||
|
||||
|
@ -171,6 +182,7 @@ private object BSONHandlers {
|
|||
follows -> o.follows,
|
||||
studies -> o.studies,
|
||||
teams -> o.teams,
|
||||
swisses -> o.swisses,
|
||||
stream -> o.stream.option(true)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -16,7 +16,8 @@ final class Env(
|
|||
studyApi: lila.study.StudyApi,
|
||||
tourLeaderApi: lila.tournament.LeaderboardApi,
|
||||
getTourName: lila.tournament.GetTourName,
|
||||
getTeamName: lila.team.GetTeamName
|
||||
getTeamName: lila.team.GetTeamName,
|
||||
swissApi: lila.swiss.SwissApi
|
||||
)(implicit
|
||||
ec: scala.concurrent.ExecutionContext,
|
||||
system: ActorSystem
|
||||
|
@ -52,7 +53,8 @@ final class Env(
|
|||
"relation",
|
||||
"startStudy",
|
||||
"streamStart",
|
||||
"gdprErase"
|
||||
"gdprErase",
|
||||
"swissFinish"
|
||||
) {
|
||||
case lila.forum.actorApi.CreatePost(post) => write.forumPost(post).unit
|
||||
case prog: lila.practice.PracticeProgress.OnComplete => write.practice(prog).unit
|
||||
|
@ -67,5 +69,6 @@ final class Env(
|
|||
case lila.hub.actorApi.team.JoinTeam(id, userId) => write.team(id, userId).unit
|
||||
case lila.hub.actorApi.streamer.StreamStart(userId) => write.streamStart(userId).unit
|
||||
case lila.user.User.GDPRErase(user) => write.erase(user).unit
|
||||
case lila.swiss.SwissFinish(swissId, ranking) => write.swiss(swissId, ranking)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
package lila.activity
|
||||
|
||||
import model._
|
||||
import ornicar.scalalib.Zero
|
||||
|
||||
import lila.rating.PerfType
|
||||
import lila.study.Study
|
||||
import lila.swiss.Swiss
|
||||
import lila.user.User
|
||||
import model._
|
||||
|
||||
object activities {
|
||||
|
||||
|
@ -103,4 +104,10 @@ object activities {
|
|||
def +(s: String) = copy(value = (s :: value).distinct take maxSubEntries)
|
||||
}
|
||||
implicit val TeamsZero = Zero.instance(Teams(Nil))
|
||||
|
||||
case class SwissRank(id: Swiss.Id, rank: Int)
|
||||
case class Swisses(value: List[SwissRank]) extends AnyVal {
|
||||
def +(s: SwissRank) = copy(value = (s :: value) take maxSubEntries)
|
||||
}
|
||||
implicit val SwissesZero = Zero.instance(Swisses(Nil))
|
||||
}
|
||||
|
|
|
@ -966,6 +966,8 @@ val `joinedNbSimuls` = new I18nKey("activity:joinedNbSimuls")
|
|||
val `createdNbStudies` = new I18nKey("activity:createdNbStudies")
|
||||
val `competedInNbTournaments` = new I18nKey("activity:competedInNbTournaments")
|
||||
val `rankedInTournament` = new I18nKey("activity:rankedInTournament")
|
||||
val `competedInNbSwissTournaments` = new I18nKey("activity:competedInNbSwissTournaments")
|
||||
val `rankedInSwissTournament` = new I18nKey("activity:rankedInSwissTournament")
|
||||
val `joinedNbTeams` = new I18nKey("activity:joinedNbTeams")
|
||||
}
|
||||
|
||||
|
|
|
@ -89,7 +89,6 @@ object Study {
|
|||
implicit val nameIso = lila.common.Iso.string[Name](Name.apply, _.value)
|
||||
|
||||
case class IdName(_id: Id, name: Name) {
|
||||
|
||||
def id = _id
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ import lila.db.BSON
|
|||
import lila.db.dsl._
|
||||
import lila.user.User
|
||||
|
||||
private object BsonHandlers {
|
||||
object BsonHandlers {
|
||||
|
||||
implicit val variantHandler = variantByKeyHandler
|
||||
implicit val clockHandler = clockConfigHandler
|
||||
|
@ -130,4 +130,7 @@ private object BsonHandlers {
|
|||
"garbage" -> s.unrealisticSettings.option(true)
|
||||
)
|
||||
}
|
||||
|
||||
import Swiss.IdName
|
||||
implicit val SwissIdNameBSONHandler = Macros.handler[IdName]
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package lila.swiss
|
||||
|
||||
import chess.Clock.{ Config => ClockConfig }
|
||||
import chess.format.FEN
|
||||
import chess.Speed
|
||||
import org.joda.time.DateTime
|
||||
import scala.concurrent.duration._
|
||||
|
@ -8,7 +9,6 @@ import scala.concurrent.duration._
|
|||
import lila.hub.LightTeam.TeamID
|
||||
import lila.rating.PerfType
|
||||
import lila.user.User
|
||||
import chess.format.FEN
|
||||
|
||||
case class Swiss(
|
||||
_id: Swiss.Id,
|
||||
|
@ -93,6 +93,10 @@ object Swiss {
|
|||
case class Performance(value: Float) extends AnyVal
|
||||
case class Score(value: Int) extends AnyVal
|
||||
|
||||
case class IdName(_id: Id, name: String) {
|
||||
def id = _id
|
||||
}
|
||||
|
||||
case class Settings(
|
||||
nbRounds: Int,
|
||||
rated: Boolean,
|
||||
|
|
|
@ -430,9 +430,9 @@ final class SwissApi(
|
|||
|
||||
private[swiss] def finish(oldSwiss: Swiss): Funit =
|
||||
Sequencing(oldSwiss.id)(startedById) { swiss =>
|
||||
colls.pairing.countSel($doc(SwissPairing.Fields.swissId -> swiss.id)) flatMap {
|
||||
case 0 => destroy(swiss)
|
||||
case _ => doFinish(swiss)
|
||||
colls.pairing.exists($doc(SwissPairing.Fields.swissId -> swiss.id)) flatMap {
|
||||
if (_) doFinish(swiss)
|
||||
else destroy(swiss)
|
||||
}
|
||||
}
|
||||
private def doFinish(swiss: Swiss): Funit =
|
||||
|
@ -459,6 +459,15 @@ final class SwissApi(
|
|||
} >>- {
|
||||
systemChat(swiss.id, s"Tournament completed!")
|
||||
socket.reload(swiss.id)
|
||||
system.scheduler
|
||||
.scheduleOnce(10 seconds) {
|
||||
// we're delaying this to make sure the ranking has been recomputed
|
||||
// since doFinish is called by finishGame before that
|
||||
rankingApi(swiss) foreach { ranking =>
|
||||
Bus.publish(SwissFinish(swiss.id, ranking), "swissFinish")
|
||||
}
|
||||
}
|
||||
.unit
|
||||
}
|
||||
|
||||
def kill(swiss: Swiss): Funit = {
|
||||
|
@ -639,6 +648,11 @@ final class SwissApi(
|
|||
.zipWithIndex
|
||||
}
|
||||
|
||||
private val idNameProjection = $doc("name" -> true)
|
||||
|
||||
def idNames(ids: List[Swiss.Id]): Fu[List[Swiss.IdName]] =
|
||||
colls.swiss.find($inIds(ids), idNameProjection.some).cursor[Swiss.IdName]().list()
|
||||
|
||||
private def Sequencing[A: Zero](
|
||||
id: Swiss.Id
|
||||
)(fetch: Swiss.Id => Fu[Option[Swiss]])(run: Swiss => Fu[A]): Fu[A] =
|
||||
|
|
|
@ -26,3 +26,5 @@ case class FeaturedSwisses(
|
|||
created: List[Swiss],
|
||||
started: List[Swiss]
|
||||
)
|
||||
|
||||
case class SwissFinish(id: Swiss.Id, ranking: Ranking)
|
||||
|
|
|
@ -4,9 +4,9 @@ import lila.user.User
|
|||
|
||||
package object swiss extends PackageObject {
|
||||
|
||||
private[swiss] val logger = lila.log("swiss")
|
||||
type Ranking = Map[lila.user.User.ID, Int]
|
||||
|
||||
private[swiss] type Ranking = Map[lila.user.User.ID, Int]
|
||||
private[swiss] val logger = lila.log("swiss")
|
||||
|
||||
// FIDE TRF player IDs
|
||||
private[swiss] type PlayerIds = Map[User.ID, Int]
|
||||
|
|
|
@ -55,13 +55,21 @@
|
|||
<item quantity="other">Created %s new studies</item>
|
||||
</plurals>
|
||||
<plurals name="competedInNbTournaments">
|
||||
<item quantity="one">Competed in %s tournament</item>
|
||||
<item quantity="other">Competed in %s tournaments</item>
|
||||
<item quantity="one">Competed in %s Arena tournament</item>
|
||||
<item quantity="other">Competed in %s Arena tournaments</item>
|
||||
</plurals>
|
||||
<plurals name="rankedInTournament">
|
||||
<item quantity="one">Ranked #%1$s (top %2$s%%) with %3$s game in %4$s</item>
|
||||
<item quantity="other">Ranked #%1$s (top %2$s%%) with %3$s games in %4$s</item>
|
||||
</plurals>
|
||||
<plurals name="competedInNbSwissTournaments">
|
||||
<item quantity="one">Competed in %s swiss tournament</item>
|
||||
<item quantity="other">Competed in %s swiss tournaments</item>
|
||||
</plurals>
|
||||
<plurals name="rankedInSwissTournament">
|
||||
<item quantity="one">Ranked #%1$s in %2$s</item>
|
||||
<item quantity="other">Ranked #%1$s in %2$s</item>
|
||||
</plurals>
|
||||
<string name="signedUp">Signed up to lichess.org</string>
|
||||
<plurals name="joinedNbTeams">
|
||||
<item quantity="one">Joined %s team</item>
|
||||
|
|
Loading…
Reference in New Issue