migration WIP

rm0193-mapreduce
Thibault Duplessis 2019-11-30 19:00:44 -06:00
parent 3cdc627520
commit 234432b226
30 changed files with 276 additions and 291 deletions

View File

@ -165,7 +165,7 @@ lazy val game = module("game", Seq(common, memo, db, hub, user, chat)).settings(
)
lazy val gameSearch = module("gameSearch", Seq(common, hub, search, game)).settings(
libraryDependencies ++= provided(play.api) ++ reactivemongo.bundle
libraryDependencies ++= provided(play.api, play.joda) ++ reactivemongo.bundle
)
lazy val tv = module("tv", Seq(common, db, hub, socket, game, round, user)).settings(
@ -270,7 +270,7 @@ lazy val studySearch = module("studySearch", Seq(common, hub, study, search)).se
)
lazy val learn = module("learn", Seq(common, db, user)).settings(
libraryDependencies ++= provided(play.api) ++ reactivemongo.bundle
libraryDependencies ++= provided(play.api, play.joda) ++ reactivemongo.bundle
)
lazy val evalCache = module("evalCache", Seq(common, db, user, security, socket, tree)).settings(

View File

@ -2,6 +2,7 @@ package lila.coach
import org.joda.time.DateTime
import scala.concurrent.duration._
import scala.concurrent.Future
import lila.db.dsl._
import lila.db.Photographer
@ -12,6 +13,7 @@ import lila.user.{ User, UserRepo }
final class CoachApi(
coachColl: Coll,
reviewColl: Coll,
userRepo: UserRepo,
photographer: Photographer,
notifyApi: NotifyApi
) {
@ -21,7 +23,7 @@ final class CoachApi(
def byId(id: Coach.Id): Fu[Option[Coach]] = coachColl.byId[Coach](id.value)
def find(username: String): Fu[Option[Coach.WithUser]] =
UserRepo named username flatMap { _ ?? find }
userRepo named username flatMap { _ ?? find }
def find(user: User): Fu[Option[Coach.WithUser]] = Granter(_.Coach)(user) ?? {
byId(Coach.Id(user.id)) map2 withUser(user)
@ -30,7 +32,7 @@ final class CoachApi(
def findOrInit(user: User): Fu[Option[Coach.WithUser]] = Granter(_.Coach)(user) ?? {
find(user) orElse {
val c = Coach.WithUser(Coach make user, user)
coachColl insert c.coach inject c.some
coachColl.insert.one(c.coach) inject c.some
}
}
@ -38,29 +40,29 @@ final class CoachApi(
Granter(_.Coach)(user) ?? coachColl.exists($id(user.id) ++ $doc("listed" -> true))
def setSeenAt(user: User): Funit =
Granter(_.Coach)(user) ?? coachColl.update($id(user.id), $set("user.seenAt" -> DateTime.now)).void
Granter(_.Coach)(user) ?? coachColl.update.one($id(user.id), $set("user.seenAt" -> DateTime.now)).void
def setRating(userPre: User): Funit =
Granter(_.Coach)(userPre) ?? {
UserRepo.byId(userPre.id) flatMap {
userRepo.byId(userPre.id) flatMap {
_ ?? { user =>
coachColl.update($id(user.id), $set("user.rating" -> user.perfs.bestStandardRating)).void
coachColl.update.one($id(user.id), $set("user.rating" -> user.perfs.bestStandardRating)).void
}
}
}
def update(c: Coach.WithUser, data: CoachProfileForm.Data): Funit =
coachColl.update(
coachColl.update.one(
$id(c.coach.id),
data(c.coach),
upsert = true
).void
def setNbReviews(id: Coach.Id, nb: Int): Funit =
coachColl.update($id(id), $set("nbReviews" -> nb)).void
coachColl.update.one($id(id), $set("nbReviews" -> nb)).void
private[coach] def toggleApproved(username: String, value: Boolean): Fu[String] =
coachColl.update(
coachColl.update.one(
$id(User.normalize(username)),
$set("approved" -> value)
) map { result =>
@ -72,11 +74,11 @@ final class CoachApi(
def uploadPicture(c: Coach.WithUser, picture: Photographer.Uploaded): Funit =
photographer(c.coach.id.value, picture).flatMap { pic =>
coachColl.update($id(c.coach.id), $set("picturePath" -> pic.path)).void
coachColl.update.one($id(c.coach.id), $set("picturePath" -> pic.path)).void
}
def deletePicture(c: Coach.WithUser): Funit =
coachColl.update($id(c.coach.id), $unset("picturePath")).void
coachColl.update.one($id(c.coach.id), $unset("picturePath")).void
private def withUser(user: User)(coach: Coach) = Coach.WithUser(coach, user)
@ -105,7 +107,7 @@ final class CoachApi(
}
if (me.troll) fuccess(review)
else {
reviewColl.update($id(id), review, upsert = true) >>
reviewColl.update.one($id(id), review, upsert = true) >>
notifyApi.addNotification(Notification.make(
notifies = Notification.Notifies(coach.id.value),
content = lila.notify.CoachReview
@ -119,14 +121,14 @@ final class CoachApi(
reviewColl.byId[CoachReview](CoachReview.makeId(user, coach))
def approve(r: CoachReview, v: Boolean) = {
if (v) reviewColl.update(
if (v) reviewColl.update.one(
$id(r.id),
$set("approved" -> v) ++ $unset("moddedAt")
).void
else reviewColl.remove($id(r.id)).void
else reviewColl.delete.one($id(r.id)).void
} >> refreshCoachNbReviews(r.coachId)
def mod(r: CoachReview) = reviewColl.update($id(r.id), $set(
def mod(r: CoachReview) = reviewColl.update.one($id(r.id), $set(
"approved" -> false,
"moddedAt" -> DateTime.now
)) >> refreshCoachNbReviews(r.coachId)
@ -149,15 +151,17 @@ final class CoachApi(
findRecent($doc("coachId" -> c.id.value))
def deleteAllBy(userId: User.ID): Funit = for {
reviews <- reviewColl.find($doc("userId" -> userId)).list[CoachReview]
_ <- reviews.map { review =>
reviewColl.remove($doc("userId" -> review.userId)).void
}.sequenceFu
_ <- reviews.map(_.coachId).distinct.map(refreshCoachNbReviews).sequenceFu
reviews <- reviewColl.ext.find($doc("userId" -> userId)).list[CoachReview]
_ <- Future.sequence(reviews.map { review =>
reviewColl.delete.one($doc("userId" -> review.userId)).void
})
_ <- Future sequence {
reviews.map(_.coachId).distinct.map(refreshCoachNbReviews)
}
} yield ()
private def findRecent(selector: Bdoc): Fu[CoachReview.Reviews] =
reviewColl.find(selector)
reviewColl.ext.find(selector)
.sort($sort desc "createdAt")
.list[CoachReview](100) map CoachReview.Reviews.apply
}

View File

@ -7,9 +7,12 @@ import lila.db.dsl._
import lila.db.paginator.{ Adapter }
import lila.user.{ User, UserRepo }
final class CoachPager(coll: Coll) {
final class CoachPager(
userRepo: UserRepo,
coll: Coll
) {
val maxPerPage = lila.common.MaxPerPage(10)
val maxPerPage = lila.common.config.MaxPerPage(10)
import CoachPager._
import BsonHandlers._
@ -21,7 +24,7 @@ final class CoachPager(coll: Coll) {
"listed" -> Coach.Listed(true),
"approved" -> Coach.Approved(true)
),
projection = $empty,
projection = none,
sort = order.predicate
) mapFutureList withUsers
Paginator(
@ -32,7 +35,7 @@ final class CoachPager(coll: Coll) {
}
private def withUsers(coaches: Seq[Coach]): Fu[Seq[Coach.WithUser]] =
UserRepo.withColl {
userRepo.withColl {
_.optionsByOrderedIds[User, User.ID](coaches.map(_.id.value), ReadPreference.secondaryPreferred)(_.id)
} map { users =>
coaches zip users collect {

View File

@ -1,36 +1,43 @@
package lila.coach
import akka.actor._
import com.softwaremill.macwire._
import io.methvin.play.autoconfig._
import play.api.Configuration
import scala.concurrent.duration.FiniteDuration
import lila.common.config._
import lila.security.Permission
import akka.actor._
import com.typesafe.config.Config
import scala.concurrent.duration._
@Module
private class CoachConfig(
@ConfigName("collection.coach") val coachColl: CollName,
@ConfigName("collection.review") val reviewColl: CollName,
@ConfigName("collection.image") val imageColl: CollName
)
final class Env(
config: Config,
appConfig: Configuration,
userRepo: lila.user.UserRepo,
notifyApi: lila.notify.NotifyApi,
system: ActorSystem,
db: lila.db.Env
) {
)(implicit system: ActorSystem) {
private val CollectionCoach = config getString "collection.coach"
private val CollectionReview = config getString "collection.review"
private val CollectionImage = config getString "collection.image"
private val config = appConfig.get[CoachConfig]("coach")(AutoConfig.loader)
private lazy val coachColl = db(CollectionCoach)
private lazy val reviewColl = db(CollectionReview)
private lazy val imageColl = db(CollectionImage)
private lazy val coachColl = db(config.coachColl)
private lazy val photographer = new lila.db.Photographer(imageColl, "coach")
private lazy val photographer = new lila.db.Photographer(db(config.imageColl), "coach")
lazy val api = new CoachApi(
coachColl = coachColl,
reviewColl = reviewColl,
userRepo = userRepo,
reviewColl = db(config.reviewColl),
photographer = photographer,
notifyApi = notifyApi
)
lazy val pager = new CoachPager(coachColl)
lazy val pager = wire[CoachPager]
lila.common.Bus.subscribeFun("adjustCheater", "finishGame", "shadowban", "setPermissions") {
case lila.hub.actorApi.mod.Shadowban(userId, true) =>
@ -56,13 +63,3 @@ final class Env(
}
}
}
object Env {
lazy val current: Env = "coach" boot new Env(
config = lila.common.PlayApp loadConfig "coach",
notifyApi = lila.notify.Env.current.api,
system = lila.common.PlayApp.system,
db = lila.db.Env.current
)
}

View File

@ -9,7 +9,7 @@ object UrlList {
case class Url(value: String) extends AnyVal
def apply(text: String): List[Url] =
text.lines.toList.map(_.trim).filter(_.nonEmpty) flatMap toUrl take max
text.linesIterator.toList.view.map(_.trim).filter(_.nonEmpty) flatMap toUrl take max to List
private val UrlRegex = """(?:youtube\.com|youtu\.be)/(?:watch)?(?:\?v=)?([^"&?/ ]{11})""".r.unanchored
@ -32,7 +32,7 @@ object UrlList {
private val UrlRegex = """(?:lichess\.org)/study/(\w{8})""".r.unanchored
def apply(text: String): List[StudyId] =
text.lines.toList.map(_.trim).filter(_.nonEmpty) flatMap toId take max
text.linesIterator.toList.view.map(_.trim).filter(_.nonEmpty) flatMap toId take max to List
private def toId(line: String): Option[StudyId] = line match {
case UrlRegex(id) => StudyId(id).some

View File

@ -1,17 +1,18 @@
package lila.coordinate
import play.api.Configuration
import com.softwaremill.macwire._
import lila.common.config.CollName
final class Env(
appConfig: Configuration,
db: lila.db.Env
) {
private val CollectionScore = appConfig.get[String]("coordinate.collection.score")
private lazy val scoreColl = db(appConfig.get[CollName]("coordinate.collection.score"))
lazy val api = new CoordinateApi(scoreColl = scoreColl)
lazy val api = wire[CoordinateApi]
lazy val forms = DataForm
private[coordinate] lazy val scoreColl = db(CollectionScore)
}

View File

@ -38,7 +38,7 @@ private object BSONHandlers {
}
}
}.flatMap {
_.toNel toTry lila.base.LilaException(s"Empty PVs ${value}")
_.toNel toTry s"Empty PVs ${value}"
}
case b => lila.db.BSON.handlerBadType[NonEmptyList[Pv]](b)
}

View File

@ -1,8 +1,9 @@
package lila.event
import play.api.Configuration
import com.softwaremill.macwire._
import lila.common.CollName
import lila.common.config.CollName
import lila.common.config._
final class Env(
@ -13,5 +14,5 @@ final class Env(
private lazy val eventColl = db(appConfig.get[CollName]("event.collection.event"))
lazy val api = new EventApi(coll = eventColl, asyncCache = asyncCache)
lazy val api = wire[EventApi]
}

View File

@ -1,48 +1,41 @@
package lila.gameSearch
import akka.actor._
import com.typesafe.config.Config
import akka.actor.ActorSystem
import com.softwaremill.macwire._
import io.methvin.play.autoconfig._
import play.api.Configuration
import lila.game.actorApi.{ InsertGame, FinishGame }
import lila.search._
import lila.common.config._
@Module
private class GameSearchConfig(
@ConfigName("index") val indexName: String,
@ConfigName("paginator.max_per_page") val paginatorMaxPerPage: MaxPerPage,
@ConfigName("actor.name") val actorName: String
)
final class Env(
config: Config,
system: ActorSystem,
appConfig: Configuration,
gameRepo: lila.game.GameRepo,
makeClient: Index => ESClient
) {
)(implicit system: ActorSystem) {
private val IndexName = config getString "index"
private val PaginatorMaxPerPage = config getInt "paginator.max_per_page"
private val ActorName = config getString "actor.name"
private val config = appConfig.get[GameSearchConfig]("gameSearch")(AutoConfig.loader)
private lazy val client = makeClient(Index(IndexName))
private lazy val client = makeClient(Index(config.indexName))
lazy val api = new GameSearchApi(client, system)
lazy val api = wire[GameSearchApi]
lazy val paginator = new PaginatorBuilder[lila.game.Game, Query](
searchApi = api,
maxPerPage = lila.common.MaxPerPage(PaginatorMaxPerPage)
)
lazy val paginator = wire[PaginatorBuilder[lila.game.Game, Query]]
lazy val forms = new DataForm
lazy val forms = wire[DataForm]
lazy val userGameSearch = new UserGameSearch(
forms = forms,
paginator = paginator
)
lazy val userGameSearch = wire[UserGameSearch]
lila.common.Bus.subscribeFun("finishGame", "gameSearchInsert") {
case FinishGame(game, _, _) if !game.aborted => api store game
case InsertGame(game) => api store game
}
}
object Env {
lazy val current = "gameSearch" boot new Env(
config = lila.common.PlayApp loadConfig "gameSearch",
system = lila.common.PlayApp.system,
makeClient = lila.search.Env.current.makeClient
)
}

View File

@ -2,7 +2,6 @@ package lila.gameSearch
import org.joda.time.DateTime
import org.joda.time.format.DateTimeFormat
import play.api.libs.iteratee._
import play.api.libs.json._
import scala.concurrent.duration._
import scala.util.Try
@ -13,12 +12,12 @@ import lila.search._
final class GameSearchApi(
client: ESClient,
system: akka.actor.ActorSystem
) extends SearchReadApi[Game, Query] {
gameRepo: GameRepo
)(implicit system: akka.actor.ActorSystem) extends SearchReadApi[Game, Query] {
def search(query: Query, from: From, size: Size) =
client.search(query, from, size) flatMap { res =>
GameRepo gamesFromSecondary res.ids
gameRepo gamesFromSecondary res.ids
}
def count(query: Query) =
@ -28,13 +27,13 @@ final class GameSearchApi(
client.search(query, From(0), Size(max)).map(_.ids)
def store(game: Game) = storable(game) ?? {
GameRepo isAnalysed game.id flatMap { analysed =>
gameRepo isAnalysed game.id flatMap { analysed =>
lila.common.Future.retry(
() => client.store(Id(game.id), toDoc(game, analysed)),
delay = 20.seconds,
retries = 2,
logger.some
)(system)
)
}
}

View File

@ -2,6 +2,7 @@ package lila.gameSearch
import chess.{ Mode, Status }
import org.joda.time.DateTime
import play.api.libs.json.JodaWrites._
import lila.rating.RatingRange
import lila.search.Range
@ -90,7 +91,7 @@ object Query {
"%d move{s}"
)
val averageRatings = (RatingRange.min to RatingRange.max by 100).toList map { e => e -> (e + " Rating") }
val averageRatings = (RatingRange.min to RatingRange.max by 100).toList map { e => e -> s"$e Rating" }
val hasAis = List(0 -> "Human opponent", 1 -> "Computer opponent")

View File

@ -1,31 +1,29 @@
package lila.history
import com.typesafe.config.Config
import com.softwaremill.macwire._
import io.methvin.play.autoconfig._
import play.api.Configuration
import scala.concurrent.duration.FiniteDuration
import lila.common.config._
@Module
private class HistoryConfig(
@ConfigName("collection.history") val historyColl: CollName,
@ConfigName("cached.rating_chart.ttl") val ratingChartTtl: FiniteDuration
)
final class Env(
config: Config,
appConfig: Configuration,
mongoCache: lila.memo.MongoCache.Builder,
db: lila.db.Env
) {
private val CachedRatingChartTtl = config duration "cached.rating_chart.ttl"
private val config = appConfig.get[HistoryConfig]("history")(AutoConfig.loader)
private val Collectionhistory = config getString "collection.history"
private lazy val coll = db(config.historyColl)
lazy val api = new HistoryApi(db(Collectionhistory))
lazy val api = wire[HistoryApi]
lazy val ratingChartApi = new RatingChartApi(
historyApi = api,
mongoCache = mongoCache,
cacheTtl = CachedRatingChartTtl
)
}
object Env {
lazy val current = "history" boot new Env(
config = lila.common.PlayApp loadConfig "history",
mongoCache = lila.memo.Env.current.mongoCache,
db = lila.db.Env.current
)
lazy val ratingChartApi = wire[RatingChartApi]
}

View File

@ -1,5 +1,7 @@
package lila.history
import scala.util.{ Try, Success, Failure }
import lila.rating.PerfType
case class History(
@ -47,16 +49,15 @@ object History {
import reactivemongo.api.bson._
private[history] implicit val RatingsMapReader = new BSONDocumentReader[RatingsMap] {
def read(doc: BSONDocument): RatingsMap = doc.stream.flatMap {
case scala.util.Success(BSONElement(k, BSONInteger(v))) => k.toIntOption map (_ -> v)
def readDocument(doc: BSONDocument) = Success(doc.elements.flatMap {
case BSONElement(k, BSONInteger(v)) => k.toIntOption map (_ -> v)
case _ => none[(Int, Int)]
}.toList sortBy (_._1)
}.sortBy(_._1) to List)
}
private[history] implicit val HistoryBSONReader = new BSONDocumentReader[History] {
def read(doc: BSONDocument): History = {
def ratingsMap(key: String): RatingsMap = ~doc.getAs[RatingsMap](key)
def readDocument(doc: BSONDocument) = Success {
def ratingsMap(key: String): RatingsMap = ~doc.getAsOpt[RatingsMap](key)
History(
standard = ratingsMap("standard"),
chess960 = ratingsMap("chess960"),

View File

@ -18,7 +18,7 @@ final class HistoryApi(coll: Coll) {
def addPuzzle(user: User, completedAt: DateTime, perf: Perf): Funit = {
val days = daysBetween(user.createdAt, completedAt)
coll.update(
coll.update.one(
$id(user.id),
$set(s"puzzle.$days" -> $int(perf.intRating)),
upsert = true
@ -47,10 +47,10 @@ final class HistoryApi(coll: Coll) {
case (k, p) => k -> p.intRating
}
val days = daysBetween(user.createdAt, game.movedAt)
coll.update(
coll.update.one(
$id(user.id),
$doc("$set" -> $doc(changes.map {
case (perf, rating) => BSONElement(s"$perf.$days", $int(rating))
case (perf, rating) => (s"$perf.$days", $int(rating))
})),
upsert = true
).void
@ -59,7 +59,7 @@ final class HistoryApi(coll: Coll) {
// used for rating refunds
def setPerfRating(user: User, perf: PerfType, rating: Int): Funit = {
val days = daysBetween(user.createdAt, DateTime.now)
coll.update(
coll.update.one(
$id(user.id),
$set(s"${perf.key}.$days" -> $int(rating))
).void
@ -91,11 +91,11 @@ final class HistoryApi(coll: Coll) {
val project = BSONDocument {
("_id" -> BSONBoolean(false)) :: days.map { d => s"${perf.key}.$d" -> BSONBoolean(true) }
}
coll.find($id(user.id), project).uno[Bdoc](ReadPreference.secondaryPreferred).map {
coll.find($id(user.id), project.some).uno[Bdoc](ReadPreference.secondaryPreferred).map {
_.flatMap {
_.getAs[Bdoc](perf.key) map {
_.stream.foldLeft(currentRating) {
case (max, scala.util.Success(BSONElement(_, BSONInteger(v)))) if v > max => v
_.child(perf.key) map {
_.elements.foldLeft(currentRating) {
case (max, BSONElement(_, BSONInteger(v))) if v > max => v
case (max, _) => max
}
}

View File

@ -1,8 +1,7 @@
package lila.learn
import reactivemongo.api.bson.{ MapReader => _, MapWriter => _, _ }
import reactivemongo.api.bson._
import lila.db.BSON
import lila.db.dsl._
object BSONHandlers {
@ -10,13 +9,11 @@ object BSONHandlers {
import StageProgress.Score
private implicit val ScoreHandler = intAnyValHandler[Score](_.value, Score.apply)
private implicit val ScoresHandler = bsonArrayToVectorHandler[Score]
private implicit val StageProgressHandler =
isoHandler[StageProgress, Vector[Score], BSONArray](
isoHandler[StageProgress, Vector[Score]](
(s: StageProgress) => s.scores, StageProgress.apply _
)(ScoresHandler)
)
private implicit val LearnProgressStagesHandler = BSON.MapValue.MapHandler[String, StageProgress]
implicit val LearnProgressIdHandler = stringAnyValHandler[LearnProgress.Id](_.value, LearnProgress.Id.apply)
implicit val LearnProgressHandler = Macros.handler[LearnProgress]
}

View File

@ -1,23 +1,15 @@
package lila.learn
import com.typesafe.config.Config
import io.methvin.play.autoconfig._
import play.api.Configuration
import lila.common.config._
final class Env(
config: Config,
appConfig: Configuration,
db: lila.db.Env
) {
private val CollectionProgress = config getString "collection.progress"
lazy val api = new LearnApi(
coll = db(CollectionProgress)
)
}
object Env {
lazy val current: Env = "learn" boot new Env(
config = lila.common.PlayApp loadConfig "learn",
db = lila.db.Env.current
coll = db(appConfig.get[CollName]("learn.collection.progress"))
)
}

View File

@ -2,6 +2,7 @@ package lila.learn
import lila.common.PimpedJson._
import play.api.libs.json._
import play.api.libs.json.JodaWrites._
object JSONHandlers {

View File

@ -11,7 +11,7 @@ final class LearnApi(coll: Coll) {
coll.uno[LearnProgress]($id(user.id)) map { _ | LearnProgress.empty(LearnProgress.Id(user.id)) }
private def save(p: LearnProgress): Funit =
coll.update($id(p.id), p, upsert = true).void
coll.update.one($id(p.id), p, upsert = true).void
def setScore(user: User, stage: String, level: Int, score: StageProgress.Score) =
get(user) flatMap { prog =>
@ -19,5 +19,5 @@ final class LearnApi(coll: Coll) {
}
def reset(user: User) =
coll.remove($id(user.id)).void
coll.delete.one($id(user.id)).void
}

View File

@ -1,36 +1,31 @@
package lila.perfStat
import akka.actor._
import com.typesafe.config.Config
import com.softwaremill.macwire._
import play.api.Configuration
import akka.actor._
import lila.common.config._
final class Env(
config: Config,
system: ActorSystem,
appConfig: Configuration,
lightUser: lila.common.LightUser.GetterSync,
gameRepo: lila.game.GameRepo,
db: lila.db.Env
) {
private val settings = new {
val CollectionPerfStat = config getString "collection.perf_stat"
}
import settings._
)(implicit system: ActorSystem) {
lazy val storage = new PerfStatStorage(
coll = db(CollectionPerfStat)
coll = db(appConfig.get[CollName]("perfStat.collection.perf_stat"))
)
lazy val indexer = new PerfStatIndexer(
storage = storage,
sequencer = new lila.hub.FutureSequencer(
system = system,
executionTimeout = None,
logger = lila.log("perfStat")
)
private lazy val sequencer = new lila.hub.FutureSequencer(
system = system,
executionTimeout = None,
logger = lila.log("perfStat")
)
lazy val jsonView = new JsonView(lightUser)
lazy val indexer = wire[PerfStatIndexer]
lazy val jsonView = wire[JsonView]
def get(user: lila.user.User, perfType: lila.rating.PerfType): Fu[PerfStat] =
storage.find(user.id, perfType) orElse {
@ -44,13 +39,3 @@ final class Env(
}
}
}
object Env {
lazy val current: Env = "perfStat" boot new Env(
config = lila.common.PlayApp loadConfig "perfStat",
system = lila.common.PlayApp.system,
lightUser = lila.user.Env.current.lightUserSync,
db = lila.db.Env.current
)
}

View File

@ -12,19 +12,6 @@ final class JsonView(getLightUser: LightUser.GetterSync) {
import JsonView._
def apply(
user: User,
stat: PerfStat,
rank: Option[Int],
percentile: Option[Double]
) = Json.obj(
"user" -> user,
"perf" -> user.perfs(stat.perfType),
"rank" -> rank,
"percentile" -> percentile,
"stat" -> stat
)
private implicit val userIdWriter: OWrites[UserId] = OWrites { u =>
val light = getLightUser(u.value)
Json.obj(
@ -43,6 +30,19 @@ final class JsonView(getLightUser: LightUser.GetterSync) {
implicit val resultStreakWrites = Json.writes[ResultStreak]
implicit val countWrites = Json.writes[Count]
implicit val perfStatWrites = Json.writes[PerfStat]
def apply(
user: User,
stat: PerfStat,
rank: Option[Int],
percentile: Option[Double]
) = Json.obj(
"user" -> user,
"perf" -> user.perfs(stat.perfType),
"rank" -> rank,
"percentile" -> percentile,
"stat" -> stat
)
}
object JsonView {

View File

@ -1,16 +1,21 @@
package lila.perfStat
import akka.actor.ActorRef
import scala.concurrent.Future
import lila.game.{ Game, GameRepo, Pov, Query }
import lila.hub.FutureSequencer
import lila.rating.PerfType
import lila.user.User
final class PerfStatIndexer(storage: PerfStatStorage, sequencer: FutureSequencer) {
final class PerfStatIndexer(
gameRepo: GameRepo,
storage: PerfStatStorage,
sequencer: FutureSequencer
) {
def userPerf(user: User, perfType: PerfType): Funit = sequencer {
GameRepo.sortedCursor(
gameRepo.sortedCursor(
Query.user(user.id) ++
Query.finished ++
Query.turnsGt(2) ++
@ -23,11 +28,13 @@ final class PerfStatIndexer(storage: PerfStatStorage, sequencer: FutureSequencer
} flatMap storage.insert recover lila.db.recoverDuplicateKey(_ => ())
}
def addGame(game: Game): Funit = game.players.flatMap { player =>
player.userId.map { userId =>
addPov(Pov(game, player), userId)
def addGame(game: Game): Funit = Future.sequence {
game.players.flatMap { player =>
player.userId.map { userId =>
addPov(Pov(game, player), userId)
}
}
}.sequenceFu.void
}.void
private def addPov(pov: Pov, userId: String): Funit = pov.game.perfType ?? { perfType =>
storage.find(userId, perfType) flatMap {

View File

@ -24,8 +24,8 @@ final class PerfStatStorage(coll: Coll) {
coll.byId[PerfStat](PerfStat.makeId(userId, perfType))
def update(perfStat: PerfStat): Funit =
coll.update($id(perfStat.id), perfStat).void
coll.update.one($id(perfStat.id), perfStat).void
def insert(perfStat: PerfStat): Funit =
coll.insert(perfStat).void
coll.insert.one(perfStat).void
}

View File

@ -187,7 +187,7 @@ final class PlaybanApi(
private def save(outcome: Outcome, userId: User.ID, rageSitDelta: Int): Funit = {
lila.mon.playban.outcome(outcome.key)()
coll.findAndUpdate(
coll.ext.findAndUpdate(
selector = $id(userId),
update = $doc(
$push("o" -> $doc("$each" -> List(outcome), "$slice" -> -30)),
@ -230,7 +230,7 @@ final class PlaybanApi(
lila.mon.playban.ban.count()
lila.mon.playban.ban.mins(ban.mins)
Bus.publish(lila.hub.actorApi.playban.Playban(record.userId, ban.mins), "playban")
coll.update(
coll.update.one(
$id(record.userId),
$unset("o") ++
$push(

View File

@ -202,5 +202,5 @@ pid(a|o|)r
чмо
""")
private def dict(words: String) = words.lines.filter(_.nonEmpty)
private def dict(words: String) = words.linesIterator.filter(_.nonEmpty)
}

View File

@ -1,26 +1,33 @@
package lila.shutup
import akka.actor._
import com.typesafe.config.Config
import com.softwaremill.macwire._
import io.methvin.play.autoconfig._
import play.api.Configuration
import lila.common.config._
import lila.user.{ User, UserRepo }
@Module
private class ShutupConfig(
@ConfigName("collection.shutup") val shutupColl: CollName,
@ConfigName("actor.name") val actorName: String
)
final class Env(
config: Config,
appConfig: Configuration,
reporter: akka.actor.ActorSelection,
follows: (String, String) => Fu[Boolean],
system: ActorSystem,
follows: (User.ID, User.ID) => Fu[Boolean],
gameRepo: lila.game.GameRepo,
userRepo: UserRepo,
db: lila.db.Env
) {
)(implicit system: ActorSystem) {
private val CollectionShutup = config getString "collection.shutup"
private val ActorName = config getString "actor.name"
private val config = appConfig.get[ShutupConfig]("shutup")(AutoConfig.loader)
lazy val api = new ShutupApi(
coll = coll,
follows = follows,
reporter = reporter
)
private lazy val coll = db(config.shutupColl)
private lazy val coll = db(CollectionShutup)
lazy val api = wire[ShutupApi]
// api actor
system.actorOf(Props(new Actor {
@ -37,16 +44,5 @@ final class Env(
case RecordPublicChat(userId, text, source) =>
api.publicChat(userId, text, source)
}
}), name = ActorName)
}
object Env {
lazy val current: Env = "shutup" boot new Env(
config = lila.common.PlayApp loadConfig "shutup",
reporter = lila.hub.Env.current.report,
system = lila.common.PlayApp.system,
follows = lila.relation.Env.current.api.fetchFollows _,
db = lila.db.Env.current
)
}), name = config.actorName)
}

View File

@ -1,6 +1,8 @@
package lila.shutup
import org.joda.time.DateTime
import scala.util.{ Try, Success, Failure }
import lila.hub.actorApi.shutup.{ PublicSource => Source }
case class PublicLine(
@ -16,32 +18,32 @@ object PublicLine {
import reactivemongo.api.bson._
import lila.db.dsl._
private implicit val SourceHandler = new BSONHandler[BSONString, Source] {
def read(bs: BSONString): Source = bs.value split ':' match {
case Array("t", id) => Source.Tournament(id)
case Array("s", id) => Source.Simul(id)
case Array("w", gameId) => Source.Watcher(gameId)
case Array("u", id) => Source.Study(id)
case a => sys error s"Invalid PublicLine source ${bs.value}"
}
def write(x: Source) = BSONString {
x match {
case Source.Tournament(id) => s"t:$id"
case Source.Simul(id) => s"s:$id"
case Source.Study(id) => s"u:$id"
case Source.Watcher(gameId) => s"w:$gameId"
private implicit val SourceHandler = lila.db.BSON.tryHandler[Source](
{
case BSONString(v) => v split ':' match {
case Array("t", id) => Success(Source.Tournament(id))
case Array("s", id) => Success(Source.Simul(id))
case Array("w", gameId) => Success(Source.Watcher(gameId))
case Array("u", id) => Success(Source.Study(id))
case a => lila.db.BSON.handlerBadValue(s"Invalid PublicLine source $v")
}
}
}
implicit val PublicLineBSONHandler = new BSONHandler[BSONValue, PublicLine] {
private val objectHandler = Macros.handler[PublicLine]
def read(bv: BSONValue): PublicLine = bv match {
case doc: BSONDocument => objectHandler read doc
case BSONString(text) => PublicLine(text, none, none)
case a => sys error s"Invalid PublicLine $a"
}
def write(x: PublicLine) =
if (x.from.isDefined) objectHandler write x
else BSONString(x.text)
}
},
x => BSONString(x match {
case Source.Tournament(id) => s"t:$id"
case Source.Simul(id) => s"s:$id"
case Source.Study(id) => s"u:$id"
case Source.Watcher(gameId) => s"w:$gameId"
})
)
private val objectHandler = Macros.handler[PublicLine]
implicit val PublicLineBSONHandler = lila.db.BSON.tryHandler[PublicLine](
{
case doc: BSONDocument => objectHandler readTry doc
case BSONString(text) => Success(PublicLine(text, none, none))
case a => lila.db.BSON.handlerBadValue(s"Invalid PublicLine $a")
},
x => if (x.from.isDefined) objectHandler.writeTry(x).get else BSONString(x.text)
)
}

View File

@ -9,6 +9,8 @@ import lila.user.{ User, UserRepo }
final class ShutupApi(
coll: Coll,
gameRepo: GameRepo,
userRepo: UserRepo,
follows: (User.ID, User.ID) => Fu[Boolean],
reporter: akka.actor.ActorSelection
) {
@ -18,9 +20,9 @@ final class ShutupApi(
import PublicLine.PublicLineBSONHandler
def getPublicLines(userId: User.ID): Fu[List[PublicLine]] =
coll.find($doc("_id" -> userId), $doc("pub" -> 1))
coll.find($doc("_id" -> userId), $doc("pub" -> 1).some)
.uno[Bdoc].map {
~_.flatMap(_.getAs[List[PublicLine]]("pub"))
~_.flatMap(_.getAsOpt[List[PublicLine]]("pub"))
}
def publicForumMessage(userId: User.ID, text: String) = record(userId, text, TextType.PublicForumMessage)
@ -28,7 +30,7 @@ final class ShutupApi(
def publicChat(userId: User.ID, text: String, source: PublicSource) = record(userId, text, TextType.PublicChat, source.some)
def privateChat(chatId: String, userId: User.ID, text: String) =
GameRepo.getSourceAndUserIds(chatId) flatMap {
gameRepo.getSourceAndUserIds(chatId) flatMap {
case (source, _) if source.has(lila.game.Source.Friend) => funit // ignore challenges
case (_, userIds) =>
record(userId, text, TextType.PrivateChat, none, userIds find (userId !=))
@ -45,7 +47,7 @@ final class ShutupApi(
toUserId: Option[User.ID] = None,
major: Boolean = false
): Funit =
UserRepo isTroll userId flatMap {
userRepo isTroll userId flatMap {
case true => funit
case false => toUserId ?? { follows(_, userId) } flatMap {
case true => funit
@ -65,12 +67,12 @@ final class ShutupApi(
"$slice" -> -textType.rotation
)
) ++ pushPublicLine
coll.findAndUpdate(
coll.ext.findAndUpdate(
selector = $id(userId),
update = $push(push),
fetchNewObject = true,
upsert = true
).map(_.value) map2 UserRecordBSONHandler.read flatMap {
).dmap(_.value flatMap UserRecordBSONHandler.readOpt) flatMap {
case None => fufail(s"can't find user record for $userId")
case Some(userRecord) => legiferate(userRecord, major)
} logFailure lila.log("shutup")
@ -81,7 +83,7 @@ final class ShutupApi(
major || userRecord.reports.exists(_.unacceptable)
} ?? {
reporter ! lila.hub.actorApi.report.Shutup(userRecord.userId, reportText(userRecord), major)
coll.update(
coll.update.one(
$id(userRecord.userId),
$unset(
TextType.PublicForumMessage.key,

View File

@ -2,55 +2,58 @@ package lila.video
import play.api.libs.ws.WSClient
import play.api.{ Mode, Configuration }
import com.softwaremill.macwire._
import io.methvin.play.autoconfig._
import play.api.Configuration
import scala.concurrent.duration._
import lila.common.CollName
import lila.common.config._
@Module
private class VideoConfig(
@ConfigName("collection.video") val videoColl: CollName,
@ConfigName("collection.view") val viewColl: CollName,
@ConfigName("sheet.url") val sheetUrl: String,
@ConfigName("sheet.delay") val sheetDelay: FiniteDuration,
@ConfigName("youtube.url") val youtubeUrl: String,
@ConfigName("youtube.api_key") val youtubeApiKey: Secret,
@ConfigName("youtube.max") val youtubeMax: Max,
@ConfigName("youtube.delay") val youtubeDelay: FiniteDuration
)
final class Env(
appConfig: Configuration,
ws: WSClient,
scheduler: akka.actor.Scheduler,
db: lila.db.Env,
asyncCache: lila.memo.AsyncCache.Builder,
ws: WSClient,
mode: Mode
) {
)(implicit system: akka.actor.ActorSystem) {
private val config = appConfig.get[Configuration]("video")
private val CollectionVideo = config.get[CollName]("collection.video")
private val CollectionView = config.get[CollName]("collection.view")
private val SheetUrl = config.get[String]("sheet.url")
private val SheetDelay = config.get[FiniteDuration]("sheet.delay")
private val YoutubeUrl = config.get[String]("youtube.url")
private val YoutubeApiKey = config.get[String]("youtube.api_key")
private val YoutubeMax = config.get[Int]("youtube.max")
private val YoutubeDelay = config.get[FiniteDuration]("youtube.delay")
private lazy val videoColl = db(CollectionVideo)
private lazy val viewColl = db(CollectionView)
private val config = appConfig.get[VideoConfig]("video")(AutoConfig.loader)
lazy val api = new VideoApi(
asyncCache = asyncCache,
videoColl = videoColl,
viewColl = viewColl
videoColl = db(config.videoColl),
viewColl = db(config.viewColl)
)
private lazy val sheet = new Sheet(ws, SheetUrl, api)
private lazy val sheet = new Sheet(ws, config.sheetUrl, api)
private lazy val youtube = new Youtube(
ws = ws,
url = YoutubeUrl,
apiKey = YoutubeApiKey,
max = YoutubeMax,
url = config.youtubeUrl,
apiKey = config.youtubeApiKey,
max = config.youtubeMax,
api = api
)
if (mode == Mode.Prod) {
scheduler.scheduleWithFixedDelay(SheetDelay, SheetDelay) { () =>
scheduler.scheduleWithFixedDelay(config.sheetDelay * 2, config.sheetDelay) { () =>
sheet.fetchAll logFailure logger nevermind
}
scheduler.scheduleWithFixedDelay(YoutubeDelay, YoutubeDelay) { () =>
scheduler.scheduleWithFixedDelay(config.youtubeDelay * 2, config.youtubeDelay) { () =>
youtube.updateAll logFailure logger nevermind
}
}

View File

@ -38,7 +38,7 @@ private[video] final class VideoApi(
object video {
private val maxPerPage = lila.common.MaxPerPage(18)
private val maxPerPage = lila.common.config.MaxPerPage(18)
def find(id: Video.ID): Fu[Option[Video]] =
videoColl.ext.find($id(id)).uno[Video]

View File

@ -5,11 +5,13 @@ import play.api.libs.json._
import play.api.libs.ws.WSClient
import scala.concurrent.Future
import lila.common.config._
private[video] final class Youtube(
ws: WSClient,
url: String,
apiKey: String,
max: Int,
apiKey: Secret,
max: Max,
api: VideoApi
) {
@ -40,10 +42,10 @@ private[video] final class Youtube(
}
private def fetch: Fu[List[Entry]] = api.video.allIds flatMap { ids =>
ws.url(url).withQueryString(
"id" -> scala.util.Random.shuffle(ids).take(max).mkString(","),
ws.url(url).withQueryStringParameters(
"id" -> scala.util.Random.shuffle(ids).take(max.value).mkString(","),
"part" -> "id,statistics,snippet,contentDetails",
"key" -> apiKey
"key" -> apiKey.value
).get() flatMap {
case res if res.status == 200 => readEntries reads res.json match {
case JsError(err) => fufail(err.toString)