add player team data to /api/tournament/:id/games - for #6756
parent
0034917aa6
commit
15072542ba
|
@ -12,6 +12,7 @@ import lila.common.config.MaxPerSecond
|
|||
import lila.common.Json.jodaWrites
|
||||
import lila.common.{ HTTPRequest, LightUser }
|
||||
import lila.db.dsl._
|
||||
import lila.team.GameTeams
|
||||
import lila.game.JsonView._
|
||||
import lila.game.PgnDump.WithFlags
|
||||
import lila.game.{ Game, PerfPicker, Query }
|
||||
|
@ -21,7 +22,9 @@ import lila.user.User
|
|||
final class GameApiV2(
|
||||
pgnDump: PgnDump,
|
||||
gameRepo: lila.game.GameRepo,
|
||||
tournamentRepo: lila.tournament.TournamentRepo,
|
||||
pairingRepo: lila.tournament.PairingRepo,
|
||||
playerRepo: lila.tournament.PlayerRepo,
|
||||
swissApi: lila.swiss.SwissApi,
|
||||
analysisRepo: lila.analyse.AnalysisRepo,
|
||||
getLightUser: LightUser.Getter
|
||||
|
@ -119,41 +122,58 @@ final class GameApiV2(
|
|||
.via(preparationFlow(config))
|
||||
|
||||
def exportByTournament(config: ByTournamentConfig): Source[String, _] =
|
||||
pairingRepo
|
||||
.sortedCursor(
|
||||
tournamentId = config.tournamentId,
|
||||
batchSize = config.perSecond.value
|
||||
)
|
||||
.documentSource()
|
||||
.grouped(config.perSecond.value)
|
||||
.throttle(1, 1 second)
|
||||
.mapAsync(1) { pairings =>
|
||||
gameRepo.gameOptionsFromSecondary(pairings.map(_.gameId)) map {
|
||||
_.zip(pairings) collect {
|
||||
case (Some(game), pairing) => game -> pairing
|
||||
Source futureSource {
|
||||
tournamentRepo.isTeamBattle(config.tournamentId) map { isTeamBattle =>
|
||||
pairingRepo
|
||||
.sortedCursor(
|
||||
tournamentId = config.tournamentId,
|
||||
batchSize = config.perSecond.value
|
||||
)
|
||||
.documentSource()
|
||||
.grouped(config.perSecond.value)
|
||||
.throttle(1, 1 second)
|
||||
.mapAsync(1) { pairings =>
|
||||
isTeamBattle.?? {
|
||||
playerRepo.teamsOfPlayers(config.tournamentId, pairings.flatMap(_.users).distinct).dmap(_.toMap)
|
||||
} flatMap { playerTeams =>
|
||||
gameRepo.gameOptionsFromSecondary(pairings.map(_.gameId)) map {
|
||||
_.zip(pairings) collect {
|
||||
case (Some(game), pairing) =>
|
||||
(
|
||||
game,
|
||||
pairing,
|
||||
playerTeams.get(pairing.user1) |@| playerTeams.get(
|
||||
pairing.user2
|
||||
) apply chess.Color.Map.apply[String]
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.mapConcat(identity)
|
||||
.mapAsync(4) {
|
||||
case (game, pairing) => enrich(config.flags)(game) dmap { _ -> pairing }
|
||||
}
|
||||
.mapAsync(4) {
|
||||
case ((game, fen, analysis), pairing) =>
|
||||
config.format match {
|
||||
case Format.PGN => pgnDump.formatter(config.flags)(game, fen, analysis)
|
||||
case Format.JSON =>
|
||||
def addBerserk(color: chess.Color)(json: JsObject) =
|
||||
if (pairing berserkOf color)
|
||||
json deepMerge Json.obj("players" -> Json.obj(color.name -> Json.obj("berserk" -> true)))
|
||||
else json
|
||||
toJson(game, fen, analysis, config.flags) dmap
|
||||
addBerserk(chess.White) dmap
|
||||
addBerserk(chess.Black) dmap { json =>
|
||||
s"${Json.stringify(json)}\n"
|
||||
.mapConcat(identity)
|
||||
.mapAsync(4) {
|
||||
case (game, pairing, teams) => enrich(config.flags)(game) dmap { (_, pairing, teams) }
|
||||
}
|
||||
.mapAsync(4) {
|
||||
case ((game, fen, analysis), pairing, teams) =>
|
||||
config.format match {
|
||||
case Format.PGN => pgnDump.formatter(config.flags)(game, fen, analysis, teams)
|
||||
case Format.JSON =>
|
||||
def addBerserk(color: chess.Color)(json: JsObject) =
|
||||
if (pairing berserkOf color)
|
||||
json deepMerge Json.obj(
|
||||
"players" -> Json.obj(color.name -> Json.obj("berserk" -> true))
|
||||
)
|
||||
else json
|
||||
toJson(game, fen, analysis, config.flags, teams) dmap
|
||||
addBerserk(chess.White) dmap
|
||||
addBerserk(chess.Black) dmap { json =>
|
||||
s"${Json.stringify(json)}\n"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def exportBySwiss(config: BySwissConfig): Source[String, _] =
|
||||
swissApi
|
||||
|
@ -169,9 +189,9 @@ final class GameApiV2(
|
|||
.mapAsync(4) {
|
||||
case (game, fen, analysis) =>
|
||||
config.format match {
|
||||
case Format.PGN => pgnDump.formatter(config.flags)(game, fen, analysis)
|
||||
case Format.PGN => pgnDump.formatter(config.flags)(game, fen, analysis, None)
|
||||
case Format.JSON =>
|
||||
toJson(game, fen, analysis, config.flags) dmap { json =>
|
||||
toJson(game, fen, analysis, config.flags, None) dmap { json =>
|
||||
s"${Json.stringify(json)}\n"
|
||||
}
|
||||
}
|
||||
|
@ -180,7 +200,9 @@ final class GameApiV2(
|
|||
private def preparationFlow(config: Config) =
|
||||
Flow[Game]
|
||||
.mapAsync(4)(enrich(config.flags))
|
||||
.mapAsync(4)(formatterFor(config).tupled)
|
||||
.mapAsync(4) {
|
||||
case (game, fen, analysis) => formatterFor(config)(game, fen, analysis, None)
|
||||
}
|
||||
|
||||
private def enrich(flags: WithFlags)(game: Game) =
|
||||
gameRepo initialFen game flatMap { initialFen =>
|
||||
|
@ -202,8 +224,8 @@ final class GameApiV2(
|
|||
}
|
||||
|
||||
private def jsonFormatter(flags: WithFlags) =
|
||||
(game: Game, initialFen: Option[FEN], analysis: Option[Analysis]) =>
|
||||
toJson(game, initialFen, analysis, flags) dmap { json =>
|
||||
(game: Game, initialFen: Option[FEN], analysis: Option[Analysis], teams: Option[GameTeams]) =>
|
||||
toJson(game, initialFen, analysis, flags, teams) dmap { json =>
|
||||
s"${Json.stringify(json)}\n"
|
||||
}
|
||||
|
||||
|
@ -211,7 +233,8 @@ final class GameApiV2(
|
|||
g: Game,
|
||||
initialFen: Option[FEN],
|
||||
analysisOption: Option[Analysis],
|
||||
withFlags: WithFlags
|
||||
withFlags: WithFlags,
|
||||
teams: Option[GameTeams] = None
|
||||
): Fu[JsObject] =
|
||||
for {
|
||||
lightUsers <- gameLightUsers(g)
|
||||
|
@ -240,6 +263,7 @@ final class GameApiV2(
|
|||
.add("provisional" -> p.provisional)
|
||||
.add("aiLevel" -> p.aiLevel)
|
||||
.add("analysis" -> analysisOption.flatMap(analysisJson.player(g pov p.color)))
|
||||
.add("team" -> teams.map(_(p.color)))
|
||||
// .add("moveCentis" -> withFlags.moveTimes ?? g.moveTimes(p.color).map(_.map(_.centis)))
|
||||
})
|
||||
)
|
||||
|
|
|
@ -3,8 +3,9 @@ package lila.api
|
|||
import chess.format.FEN
|
||||
import chess.format.pgn.Pgn
|
||||
import lila.analyse.{ Analysis, Annotator }
|
||||
import lila.game.PgnDump.WithFlags
|
||||
import lila.game.Game
|
||||
import lila.game.PgnDump.WithFlags
|
||||
import lila.team.GameTeams
|
||||
|
||||
final class PgnDump(
|
||||
val dumper: lila.game.PgnDump,
|
||||
|
@ -16,8 +17,14 @@ final class PgnDump(
|
|||
|
||||
implicit private val lang = lila.i18n.defaultLang
|
||||
|
||||
def apply(game: Game, initialFen: Option[FEN], analysis: Option[Analysis], flags: WithFlags): Fu[Pgn] =
|
||||
dumper(game, initialFen, flags) flatMap { pgn =>
|
||||
def apply(
|
||||
game: Game,
|
||||
initialFen: Option[FEN],
|
||||
analysis: Option[Analysis],
|
||||
flags: WithFlags,
|
||||
teams: Option[GameTeams] = None
|
||||
): Fu[Pgn] =
|
||||
dumper(game, initialFen, flags, teams) flatMap { pgn =>
|
||||
if (flags.tags) (game.simulId ?? simulApi.idToName) map { simulName =>
|
||||
simulName
|
||||
.orElse(game.tournamentId flatMap getTournamentName.get)
|
||||
|
@ -52,11 +59,17 @@ final class PgnDump(
|
|||
}
|
||||
|
||||
def formatter(flags: WithFlags) =
|
||||
(game: Game, initialFen: Option[FEN], analysis: Option[Analysis]) =>
|
||||
toPgnString(game, initialFen, analysis, flags)
|
||||
(game: Game, initialFen: Option[FEN], analysis: Option[Analysis], teams: Option[GameTeams]) =>
|
||||
toPgnString(game, initialFen, analysis, flags, teams)
|
||||
|
||||
def toPgnString(game: Game, initialFen: Option[FEN], analysis: Option[Analysis], flags: WithFlags) =
|
||||
apply(game, initialFen, analysis, flags) dmap { pgn =>
|
||||
def toPgnString(
|
||||
game: Game,
|
||||
initialFen: Option[FEN],
|
||||
analysis: Option[Analysis],
|
||||
flags: WithFlags,
|
||||
teams: Option[GameTeams] = None
|
||||
) =
|
||||
apply(game, initialFen, analysis, flags, teams) dmap { pgn =>
|
||||
// merge analysis & eval comments
|
||||
// 1. e4 { [%eval 0.17] } { [%clk 0:00:30] }
|
||||
// 1. e4 { [%eval 0.17] [%clk 0:00:30] }
|
||||
|
|
|
@ -15,12 +15,17 @@ final class PgnDump(
|
|||
|
||||
import PgnDump._
|
||||
|
||||
def apply(game: Game, initialFen: Option[FEN], flags: WithFlags): Fu[Pgn] = {
|
||||
def apply(
|
||||
game: Game,
|
||||
initialFen: Option[FEN],
|
||||
flags: WithFlags,
|
||||
teams: Option[Color.Map[String]] = None
|
||||
): Fu[Pgn] = {
|
||||
val imported = game.pgnImport.flatMap { pgni =>
|
||||
Parser.full(pgni.pgn).toOption
|
||||
}
|
||||
val tagsFuture =
|
||||
if (flags.tags) tags(game, initialFen, imported, withOpening = flags.opening)
|
||||
if (flags.tags) tags(game, initialFen, imported, withOpening = flags.opening, teams = teams)
|
||||
else fuccess(Tags(Nil))
|
||||
tagsFuture map { ts =>
|
||||
val turns = flags.moves ?? {
|
||||
|
@ -75,7 +80,8 @@ final class PgnDump(
|
|||
game: Game,
|
||||
initialFen: Option[FEN],
|
||||
imported: Option[ParsedPgn],
|
||||
withOpening: Boolean
|
||||
withOpening: Boolean,
|
||||
teams: Option[Color.Map[String]] = None
|
||||
): Fu[Tags] =
|
||||
gameLightUsers(game) map {
|
||||
case (wu, bu) =>
|
||||
|
@ -110,6 +116,8 @@ final class PgnDump(
|
|||
bu.flatMap(_.title).map { t =>
|
||||
Tag(_.BlackTitle, t)
|
||||
},
|
||||
teams.map { t => Tag("WhiteTeam", t.white) },
|
||||
teams.map { t => Tag("BlackTeam", t.black) },
|
||||
Tag(_.Variant, game.variant.name.capitalize).some,
|
||||
Tag.timeControl(game.clock.map(_.config)).some,
|
||||
Tag(_.ECO, game.opening.fold("?")(_.opening.eco)).some,
|
||||
|
|
|
@ -3,4 +3,6 @@ package lila
|
|||
package object team extends PackageObject {
|
||||
|
||||
private[team] def logger = lila.log("team")
|
||||
|
||||
type GameTeams = chess.Color.Map[Team.ID]
|
||||
}
|
||||
|
|
|
@ -155,7 +155,7 @@ final class PlayerRepo(coll: Coll)(implicit ec: scala.concurrent.ExecutionContex
|
|||
def countTeamPlayers(tourId: Tournament.ID, teamId: TeamID): Fu[Int] =
|
||||
coll.countSel($doc("tid" -> tourId, "t" -> teamId))
|
||||
|
||||
def teamsOfPlayers(tourId: Tournament.ID, userIds: List[User.ID]): Fu[List[(User.ID, TeamID)]] =
|
||||
def teamsOfPlayers(tourId: Tournament.ID, userIds: Seq[User.ID]): Fu[List[(User.ID, TeamID)]] =
|
||||
coll.ext
|
||||
.find($doc("tid" -> tourId, "uid" $in userIds), $doc("_id" -> false, "uid" -> true, "t" -> true))
|
||||
.list[Bdoc]()
|
||||
|
|
|
@ -183,6 +183,9 @@ final class TournamentRepo(val coll: Coll, playerCollName: CollName)(implicit
|
|||
def teamBattleOf(tourId: Tournament.ID): Fu[Option[TeamBattle]] =
|
||||
coll.primitiveOne[TeamBattle]($id(tourId), "teamBattle")
|
||||
|
||||
def isTeamBattle(tourId: Tournament.ID): Fu[Boolean] =
|
||||
coll.exists($id(tourId) ++ $doc("teamBattle" $exists true))
|
||||
|
||||
def featuredGameId(tourId: Tournament.ID) = coll.primitiveOne[Game.ID]($id(tourId), "featured")
|
||||
|
||||
private def startingSoonSelect(aheadMinutes: Int) =
|
||||
|
|
Loading…
Reference in New Issue