activity: aggregate games, computer analysis, and forum posts
This commit is contained in:
parent
df5396d2f8
commit
f2402e4693
|
@ -7,14 +7,13 @@ import lila.rating.PerfType
|
|||
import lila.user.User
|
||||
|
||||
case class Activity(
|
||||
_id: Activity.Id,
|
||||
id: Activity.Id,
|
||||
games: Activity.Games,
|
||||
tours: Activity.Tours,
|
||||
comps: Activity.CompAnalysis,
|
||||
posts: Activity.Posts
|
||||
) {
|
||||
|
||||
def id = _id
|
||||
def userId = id.userId
|
||||
def day = id.day
|
||||
}
|
||||
|
@ -45,11 +44,11 @@ object Activity {
|
|||
case class Score(win: Int, loss: Int, draw: Int, rd: RatingDiff) {
|
||||
def add(s: Score) = copy(win = win + s.win, loss = loss + s.loss, draw = draw + s.draw, rd = rd + s.rd)
|
||||
}
|
||||
case class RatingDiff(by100: Int) extends AnyVal {
|
||||
def real = by100 / 100d
|
||||
def +(r: RatingDiff) = RatingDiff(by100 + r.by100)
|
||||
case class RatingDiff(value: Int) extends AnyVal {
|
||||
def +(r: RatingDiff) = RatingDiff(value + r.value)
|
||||
}
|
||||
implicit val ScoreZero = Zero.instance(Score(0, 0, 0, RatingDiff(0)))
|
||||
implicit val RatingDiffZero = Zero.instance(RatingDiff(0))
|
||||
implicit val ScoreZero = Zero.instance(Score(0, 0, 0, RatingDiffZero.zero))
|
||||
|
||||
case class Tours(value: Map[Tours.TourId, Tours.Result]) extends AnyVal
|
||||
object Tours {
|
||||
|
@ -58,21 +57,26 @@ object Activity {
|
|||
}
|
||||
implicit val ToursZero = Zero.instance(Tours(Map.empty))
|
||||
|
||||
case class Posts(posts: Map[Posts.ThreadId, List[Posts.PostId]]) {
|
||||
case class Posts(posts: Map[Posts.TopicId, List[Posts.PostId]]) {
|
||||
def total = posts.foldLeft(0)(_ + _._2.size)
|
||||
def +(postId: Posts.PostId, topicId: Posts.TopicId) = Posts {
|
||||
posts + (topicId -> (postId :: ~posts.get(topicId)))
|
||||
}
|
||||
}
|
||||
object Posts {
|
||||
case class ThreadId(value: String) extends AnyVal
|
||||
case class TopicId(value: String) extends AnyVal
|
||||
case class PostId(value: String) extends AnyVal
|
||||
}
|
||||
implicit val PostsZero = Zero.instance(Posts(Map.empty))
|
||||
|
||||
case class CompAnalysis(gameIds: List[GameId]) extends AnyVal
|
||||
case class CompAnalysis(gameIds: List[GameId]) extends AnyVal {
|
||||
def +(gameId: GameId) = CompAnalysis(gameId :: gameIds)
|
||||
}
|
||||
case class GameId(value: String) extends AnyVal
|
||||
implicit val CompsZero = Zero.instance(CompAnalysis(Nil))
|
||||
|
||||
def make(userId: User.ID) = Activity(
|
||||
_id = Id today userId,
|
||||
id = Id today userId,
|
||||
games = GamesZero.zero,
|
||||
tours = ToursZero.zero,
|
||||
posts = PostsZero.zero,
|
||||
|
|
|
@ -1,22 +1,29 @@
|
|||
package lila.activity
|
||||
|
||||
import lila.analyse.Analysis
|
||||
import lila.game.Game
|
||||
|
||||
private object ActivityAggregation {
|
||||
|
||||
import Activity._
|
||||
|
||||
def addGame(ua: Activity.WithUserId, game: Game): Option[Activity] = for {
|
||||
def addGame(game: Game, userId: String)(a: Activity): Option[Activity] = for {
|
||||
pt <- game.perfType
|
||||
player <- game playerByUserId ua.userId
|
||||
won = game.winnerUserId map (ua.userId ==)
|
||||
player <- game playerByUserId userId
|
||||
won = game.winnerColor map (player.color ==)
|
||||
score = Score(
|
||||
win = won.has(true) ?? 1,
|
||||
loss = won.has(false) ?? 1,
|
||||
draw = won.isEmpty ?? 1,
|
||||
rd = RatingDiff(~player.ratingDiff * 100)
|
||||
rd = RatingDiff(~player.ratingDiff)
|
||||
)
|
||||
} yield ua.activity.copy(
|
||||
games = ua.activity.games.add(pt, score)
|
||||
)
|
||||
} yield a.copy(games = a.games.add(pt, score))
|
||||
|
||||
def addAnalysis(analysis: Analysis)(a: Activity): Option[Activity] =
|
||||
a.copy(comps = a.comps + GameId(analysis.id)).some
|
||||
|
||||
def addForumPost(post: lila.forum.Post, topic: lila.forum.Topic)(a: Activity) =
|
||||
post.userId map { userId =>
|
||||
a.copy(posts = a.posts.+(Posts.PostId(post.id), Posts.TopicId(topic.id)))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,21 +1,34 @@
|
|||
package lila.activity
|
||||
|
||||
import lila.analyse.Analysis
|
||||
import lila.db.dsl._
|
||||
import lila.game.Game
|
||||
import lila.user.User
|
||||
import lila.user.UserRepo.lichessId
|
||||
|
||||
final class ActivityApi(coll: Coll) {
|
||||
|
||||
import Activity._
|
||||
import BSONHandlers._
|
||||
|
||||
def get(userId: User.ID) = coll.byId[Activity, Id](Id today userId)
|
||||
|
||||
def addGame(game: Game): Funit = game.userIds.map { userId =>
|
||||
getOrCreate(userId) flatMap { old =>
|
||||
ActivityAggregation.addGame(Activity.WithUserId(old, userId), game) ?? save
|
||||
}
|
||||
update(userId) { ActivityAggregation.addGame(game, userId) _ }
|
||||
}.sequenceFu.void
|
||||
|
||||
def get(userId: User.ID) = coll.byId[Activity, Id](Id today userId)
|
||||
def getOrCreate(userId: User.ID) = get(userId) map { _ | Activity.make(userId) }
|
||||
def save(activity: Activity) = coll.update($id(activity.id), activity, upsert = true).void
|
||||
def addAnalysis(analysis: Analysis): Funit = analysis.uid.filter(lichessId !=) ?? { userId =>
|
||||
update(userId) { ActivityAggregation.addAnalysis(analysis) _ }
|
||||
}
|
||||
|
||||
def addForumPost(post: lila.forum.Post, topic: lila.forum.Topic): Funit = post.userId.filter(lichessId !=) ?? { userId =>
|
||||
update(userId) { ActivityAggregation.addForumPost(post, topic) _ }
|
||||
}
|
||||
|
||||
private def getOrCreate(userId: User.ID) = get(userId) map { _ | Activity.make(userId) }
|
||||
private def save(activity: Activity) = coll.update($id(activity.id), activity, upsert = true).void
|
||||
private def update(userId: User.ID)(f: Activity => Option[Activity]): Funit =
|
||||
getOrCreate(userId) flatMap { old =>
|
||||
f(old) ?? save
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,8 +23,28 @@ private object BSONHandlers {
|
|||
def write(id: Id) = BSONString(s"${id.userId}$sep${id.day.value}")
|
||||
}
|
||||
|
||||
private implicit val ratingDiffHandler = intAnyValHandler[RatingDiff](_.by100, RatingDiff.apply)
|
||||
private implicit val scoreHandler = Macros.handler[Score]
|
||||
private implicit val ratingDiffHandler = intAnyValHandler[RatingDiff](_.value, RatingDiff.apply)
|
||||
|
||||
implicit val scoreHandler = new lila.db.BSON[Score] {
|
||||
private val win = "w"
|
||||
private val loss = "l"
|
||||
private val draw = "d"
|
||||
private val rd = "r"
|
||||
|
||||
def reads(r: lila.db.BSON.Reader) = Score(
|
||||
win = r.intD(win),
|
||||
loss = r.intD(loss),
|
||||
draw = r.intD(draw),
|
||||
rd = r.getD[RatingDiff](rd)
|
||||
)
|
||||
|
||||
def writes(w: lila.db.BSON.Writer, o: Score) = BSONDocument(
|
||||
win -> w.intO(o.win),
|
||||
loss -> w.intO(o.loss),
|
||||
draw -> w.intO(o.draw),
|
||||
rd -> w.zero(o.rd)
|
||||
)
|
||||
}
|
||||
|
||||
private implicit val gamesMapHandler = MapDocument.MapHandler[PerfType, Score]
|
||||
private implicit val gamesHandler = isoHandler[Games, Map[PerfType, Score], Bdoc]((g: Games) => g.value, Games.apply _)
|
||||
|
@ -41,11 +61,11 @@ private object BSONHandlers {
|
|||
private implicit val gameIdsHandler = bsonArrayToListHandler[GameId]
|
||||
private implicit val compAnalysisHandler = isoHandler[CompAnalysis, List[GameId], Barr]((c: CompAnalysis) => c.gameIds, CompAnalysis.apply _)
|
||||
|
||||
private implicit val threadIdIso = Iso.string[Posts.ThreadId](Posts.ThreadId.apply, _.value)
|
||||
private implicit val topicIdIso = Iso.string[Posts.TopicId](Posts.TopicId.apply, _.value)
|
||||
private implicit val postIdHandler = stringAnyValHandler[Posts.PostId](_.value, Posts.PostId.apply)
|
||||
private implicit val postIdsHandler = bsonArrayToListHandler[Posts.PostId]
|
||||
private implicit val postsMapHandler = MapValue.MapHandler[Posts.ThreadId, List[Posts.PostId]]
|
||||
private implicit val postsHandler = isoHandler[Posts, Map[Posts.ThreadId, List[Posts.PostId]], Bdoc]((p: Posts) => p.posts, Posts.apply _)
|
||||
private implicit val postsMapHandler = MapValue.MapHandler[Posts.TopicId, List[Posts.PostId]]
|
||||
private implicit val postsHandler = isoHandler[Posts, Map[Posts.TopicId, List[Posts.PostId]], Bdoc]((p: Posts) => p.posts, Posts.apply _)
|
||||
|
||||
implicit val activityHandler = new lila.db.BSON[Activity] {
|
||||
|
||||
|
@ -56,7 +76,7 @@ private object BSONHandlers {
|
|||
private val posts = "p"
|
||||
|
||||
def reads(r: lila.db.BSON.Reader) = Activity(
|
||||
_id = r.get[Id](id),
|
||||
id = r.get[Id](id),
|
||||
games = r.getD[Games](games),
|
||||
tours = r.getD[Tours](tours),
|
||||
comps = r.getD[CompAnalysis](comps),
|
||||
|
|
|
@ -19,8 +19,10 @@ final class Env(
|
|||
system.lilaBus.subscribe(system.actorOf(Props(new Actor {
|
||||
def receive = {
|
||||
case lila.game.actorApi.FinishGame(game, _, _) if !game.aborted => api addGame game
|
||||
case lila.analyse.actorApi.AnalysisReady(_, analysis) => api addAnalysis analysis
|
||||
case lila.forum.actorApi.CreatePost(post, topic) => api.addForumPost(post, topic)
|
||||
}
|
||||
})), 'finishGame)
|
||||
})), 'finishGame, 'analysisReady, 'forumPost)
|
||||
}
|
||||
|
||||
object Env {
|
||||
|
|
|
@ -60,7 +60,8 @@ final class Env(
|
|||
shutup = shutup,
|
||||
timeline = hub.actor.timeline,
|
||||
detectLanguage = detectLanguage,
|
||||
mentionNotifier = mentionNotifier
|
||||
mentionNotifier = mentionNotifier,
|
||||
bus = system.lilaBus
|
||||
)
|
||||
|
||||
lazy val forms = new DataForm(hub.actor.captcher)
|
||||
|
|
|
@ -2,7 +2,6 @@ package lila.forum
|
|||
|
||||
import actorApi._
|
||||
import akka.actor.ActorSelection
|
||||
import org.joda.time.DateTime
|
||||
import lila.common.paginator._
|
||||
import lila.db.dsl._
|
||||
import lila.db.paginator._
|
||||
|
@ -10,6 +9,7 @@ import lila.hub.actorApi.timeline.{ ForumPost, Propagate }
|
|||
import lila.mod.ModlogApi
|
||||
import lila.security.{ Granter => MasterGranter }
|
||||
import lila.user.{ User, UserContext }
|
||||
import org.joda.time.DateTime
|
||||
|
||||
final class PostApi(
|
||||
env: Env,
|
||||
|
@ -19,7 +19,8 @@ final class PostApi(
|
|||
shutup: ActorSelection,
|
||||
timeline: ActorSelection,
|
||||
detectLanguage: lila.common.DetectLanguage,
|
||||
mentionNotifier: MentionNotifier
|
||||
mentionNotifier: MentionNotifier,
|
||||
bus: lila.common.Bus
|
||||
) {
|
||||
|
||||
import BSONHandlers._
|
||||
|
@ -68,7 +69,9 @@ final class PostApi(
|
|||
))
|
||||
}
|
||||
lila.mon.forum.post.create()
|
||||
} >>- mentionNotifier.notifyMentionedUsers(post, topic) inject post
|
||||
mentionNotifier.notifyMentionedUsers(post, topic)
|
||||
bus.publish(actorApi.CreatePost(post, topic), 'forumPost)
|
||||
} inject post
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,3 +4,5 @@ package actorApi
|
|||
case class InsertPost(post: Post)
|
||||
case class RemovePost(id: String)
|
||||
case class RemovePosts(ids: List[String])
|
||||
|
||||
case class CreatePost(post: Post, topic: Topic)
|
||||
|
|
|
@ -193,7 +193,7 @@ object ApplicationBuild extends Build {
|
|||
libraryDependencies ++= provided(play.api, reactivemongo.driver)
|
||||
)
|
||||
|
||||
lazy val activity = project("activity", Seq(common, game, user)).settings(
|
||||
lazy val activity = project("activity", Seq(common, game, analyse, user, forum, study, pool, puzzle)).settings(
|
||||
libraryDependencies ++= provided(play.api, reactivemongo.driver)
|
||||
)
|
||||
|
||||
|
|
Loading…
Reference in a new issue