/api/tournament/{id}/games endpoint - closes #4656

pull/4691/head
Thibault Duplessis 2018-10-20 09:48:18 +02:00
parent 143e98d5b6
commit a54853e5a8
5 changed files with 58 additions and 5 deletions

View File

@ -7,10 +7,10 @@ import play.api.libs.json._
import play.api.mvc._
import scala.concurrent.duration._
import lila.api.Context
import lila.api.{ Context, GameApiV2 }
import lila.app._
import lila.common.PimpedJson._
import lila.common.{ HTTPRequest, IpAddress, MaxPerPage }
import lila.common.{ HTTPRequest, IpAddress, MaxPerPage, MaxPerSecond }
object Api extends LilaController {
@ -229,14 +229,33 @@ object Api extends LilaController {
}
def tournament(id: String) = ApiRequest { req =>
val page = (getInt("page", req) | 1) atLeast 1 atMost 200
lila.tournament.TournamentRepo byId id flatMap {
_ ?? { tour =>
val page = (getInt("page", req) | 1) atLeast 1 atMost 200
Env.tournament.jsonView(tour, page.some, none, { _ => fuccess(Nil) }, none, none, lila.i18n.defaultLang) map some
}
} map toApiResult
}
def tournamentGames(id: String) = Action.async { req =>
lila.tournament.TournamentRepo byId id flatMap {
_ ?? { tour =>
GlobalLinearLimitPerIP(HTTPRequest lastRemoteAddress req) {
val format = GameApiV2.Format byRequest req
val config = GameApiV2.ByTournamentConfig(
tournamentId = tour.id,
format = GameApiV2.Format byRequest req,
flags = Game.requestPgnFlags(req, extended = false),
perSecond = MaxPerSecond(20)
)
Ok.chunked(Env.api.gameApiV2.exportByTournament(config)).withHeaders(
CONTENT_TYPE -> Game.gameContentType(config)
).fuccess
}
}
}
}
def gamesByUsersStream = Action.async(parse.tolerantText) { req =>
val userIds = req.body.split(',').take(300).toSet map lila.user.User.normalize
jsonStream(Env.game.gamesByUsersStream(userIds)).fuccess

View File

@ -122,7 +122,7 @@ object Game extends LilaController {
}
}
private def requestPgnFlags(req: RequestHeader, extended: Boolean) =
private[controllers] def requestPgnFlags(req: RequestHeader, extended: Boolean) =
lila.game.PgnDump.WithFlags(
moves = getBoolOpt("moves", req) | true,
tags = getBoolOpt("tags", req) | true,
@ -132,7 +132,7 @@ object Game extends LilaController {
literate = getBoolOpt("literate", req) | false
)
private def gameContentType(config: GameApiV2.Config) = config.format match {
private[controllers] def gameContentType(config: GameApiV2.Config) = config.format match {
case GameApiV2.Format.PGN => pgnContentType
case GameApiV2.Format.JSON => config match {
case _: GameApiV2.OneConfig => JSON

View File

@ -502,6 +502,7 @@ GET /api/game/:id controllers.Api.game(id: String)
GET /api/games/team/:teamId controllers.Api.gamesVsTeam(teamId: String)
GET /api/tournament controllers.Api.currentTournaments
GET /api/tournament/:id controllers.Api.tournament(id: String)
GET /api/tournament/:id/games controllers.Api.tournamentGames(id: String)
POST /api/tournament controllers.Tournament.apiCreate
GET /api/status controllers.Api.status
GET /api/socket controllers.Main.apiWebsocket

View File

@ -14,6 +14,7 @@ import lila.db.dsl._
import lila.game.JsonView._
import lila.game.PgnDump.WithFlags
import lila.game.{ Game, GameRepo, Query, PerfPicker }
import lila.tournament.Tournament
import lila.user.User
final class GameApiV2(
@ -86,6 +87,19 @@ final class GameApiV2(
Enumeratee.mapM(enrich(config.flags)) &>
formatterFor(config)
def exportByTournament(config: ByTournamentConfig): Enumerator[String] =
lila.tournament.PairingRepo.sortedGameIdsCursor(
tournamentId = config.tournamentId,
batchSize = config.perSecond.value
).bulkEnumerator() &>
Enumeratee.mapM { pairingDocs =>
GameRepo.gamesFromSecondary(pairingDocs.flatMap { _.getAs[Game.ID]("_id") }.toSeq)
} &>
lila.common.Iteratee.delay(1 second) &>
Enumeratee.mapConcat(_.toSeq) &>
Enumeratee.mapM(enrich(config.flags)) &>
formatterFor(config)
private def enrich(flags: WithFlags)(game: Game) =
GameRepo initialFen game flatMap { initialFen =>
(flags.evals ?? AnalysisRepo.byGame(game)) map { analysis =>
@ -200,4 +214,11 @@ object GameApiV2 {
flags: WithFlags,
perSecond: MaxPerSecond
) extends Config
case class ByTournamentConfig(
tournamentId: Tournament.ID,
format: Format,
flags: WithFlags,
perSecond: MaxPerSecond
) extends Config
}

View File

@ -2,6 +2,7 @@ package lila.tournament
import org.joda.time.DateTime
import reactivemongo.bson._
import reactivemongo.api.{ CursorProducer, ReadPreference }
import scala.collection.breakOut
import BSONHandlers._
@ -138,6 +139,17 @@ object PairingRepo {
).void
}
def sortedGameIdsCursor(
tournamentId: Tournament.ID,
batchSize: Int = 0,
readPreference: ReadPreference = ReadPreference.secondaryPreferred
)(implicit cp: CursorProducer[Bdoc]) = {
val query = coll
.find(selectTour(tournamentId), $id(true))
.sort(recentSort)
query.copy(options = query.options.batchSize(batchSize)).cursor[Bdoc](readPreference)
}
private[tournament] def playingUserIds(tour: Tournament): Fu[Set[User.ID]] =
coll.distinct[User.ID, Set]("u", Some(selectTour(tour.id) ++ selectPlaying))