138 lines
4.3 KiB
Scala
138 lines
4.3 KiB
Scala
package controllers
|
|
|
|
import play.api.data._
|
|
import play.api.data.Forms._
|
|
import play.api.mvc._
|
|
import scala.concurrent.duration._
|
|
import views._
|
|
|
|
import lila.api.Context
|
|
import lila.app._
|
|
import lila.common.HTTPRequest
|
|
import lila.db.dsl._
|
|
import lila.api.GameApiV2
|
|
import lila.common.config
|
|
import lila.user.Holder
|
|
|
|
final class GameMod(env: Env) extends LilaController(env) {
|
|
|
|
import GameMod._
|
|
|
|
def index(username: String) =
|
|
SecureBody(_.Hunter) { implicit ctx => me =>
|
|
OptionFuResult(env.user.repo named username) { user =>
|
|
implicit def req = ctx.body
|
|
val form = filterForm.bindFromRequest()
|
|
val filter = form.fold(_ => emptyFilter, identity)
|
|
env.tournament.leaderboardApi.recentByUser(user, 1) zip
|
|
env.activity.read.recentSwissRanks(user.id) zip
|
|
env.game.gameRepo.recentPovsByUserFromSecondary(
|
|
user,
|
|
100,
|
|
toDbSelect(filter) ++ lila.game.Query.finished
|
|
) flatMap { case ((arenas, swisses), povs) =>
|
|
env.mod.assessApi.makeAndGetFullOrBasicsFor(povs) map { games =>
|
|
Ok(views.html.mod.games(user, form, games, arenas.currentPageResults, swisses))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
def post(username: String) =
|
|
SecureBody(_.Hunter) { implicit ctx => me =>
|
|
OptionFuResult(env.user.repo named username) { user =>
|
|
implicit val body = ctx.body
|
|
actionForm
|
|
.bindFromRequest()
|
|
.fold(
|
|
err => BadRequest(err.toString).fuccess,
|
|
{
|
|
case (gameIds, Some("pgn")) => downloadPgn(user, gameIds).fuccess
|
|
case (gameIds, Some("analyse") | None) => multipleAnalysis(me, gameIds)
|
|
case _ => notFound
|
|
}
|
|
)
|
|
}
|
|
}
|
|
|
|
private def multipleAnalysis(me: Holder, gameIds: Seq[lila.game.Game.ID])(implicit ctx: Context) =
|
|
env.game.gameRepo.unanalysedGames(gameIds).flatMap { games =>
|
|
games.map { game =>
|
|
env.fishnet.analyser(
|
|
game,
|
|
lila.fishnet.Work.Sender(
|
|
userId = me.id,
|
|
ip = HTTPRequest.ipAddress(ctx.req).some,
|
|
mod = true,
|
|
system = false
|
|
)
|
|
)
|
|
}.sequenceFu >> env.fishnet.awaiter(games.map(_.id), 2 minutes)
|
|
} inject NoContent
|
|
|
|
private def downloadPgn(user: lila.user.User, gameIds: Seq[lila.game.Game.ID]) =
|
|
Ok.chunked {
|
|
env.api.gameApiV2.exportByIds(
|
|
GameApiV2.ByIdsConfig(
|
|
ids = gameIds,
|
|
format = GameApiV2.Format.PGN,
|
|
flags = lila.game.PgnDump.WithFlags(),
|
|
perSecond = config.MaxPerSecond(100),
|
|
playerFile = none
|
|
)
|
|
)
|
|
}.withHeaders(
|
|
noProxyBufferHeader,
|
|
CONTENT_DISPOSITION -> s"attachment; filename=lichess_mod_${user.username}_${gameIds.size}_games.pgn"
|
|
).as(pgnContentType)
|
|
|
|
private def guessSwisses(user: lila.user.User): Fu[Seq[lila.swiss.Swiss]] = fuccess(Nil)
|
|
}
|
|
|
|
object GameMod {
|
|
|
|
case class Filter(arena: Option[String], swiss: Option[String], opponents: Option[String]) {
|
|
def opponentIds: List[lila.user.User.ID] =
|
|
(~opponents)
|
|
.take(800)
|
|
.replace(",", " ")
|
|
.split(' ')
|
|
.view
|
|
.flatMap(_.trim.some.filter(_.nonEmpty))
|
|
.filter(lila.user.User.couldBeUsername)
|
|
.map(lila.user.User.normalize)
|
|
.toList
|
|
.distinct
|
|
}
|
|
|
|
val emptyFilter = Filter(none, none, none)
|
|
|
|
def toDbSelect(filter: Filter): Bdoc =
|
|
lila.game.Query.notSimul ++ lila.game.Query.clock(true) ++ filter.arena.?? { id =>
|
|
$doc(lila.game.Game.BSONFields.tournamentId -> id)
|
|
} ++ filter.swiss.?? { id =>
|
|
$doc(lila.game.Game.BSONFields.swissId -> id)
|
|
} ++ (filter.opponentIds match {
|
|
case Nil => $empty
|
|
case List(id) => $and(lila.game.Game.BSONFields.playerUids $eq id)
|
|
case ids => $and(lila.game.Game.BSONFields.playerUids $in ids)
|
|
})
|
|
|
|
val filterForm =
|
|
Form(
|
|
mapping(
|
|
"arena" -> optional(nonEmptyText),
|
|
"swiss" -> optional(nonEmptyText),
|
|
"opponents" -> optional(nonEmptyText)
|
|
)(Filter.apply)(Filter.unapply _)
|
|
)
|
|
|
|
val actionForm =
|
|
Form(
|
|
tuple(
|
|
"game" -> list(nonEmptyText),
|
|
"action" -> optional(lila.common.Form.stringIn(Set("pgn", "analyse")))
|
|
)
|
|
)
|
|
}
|