refactor activities and aggregate learn stages
This commit is contained in:
parent
72a582b221
commit
7e14de5f7b
|
@ -33,7 +33,8 @@ object Learn extends LilaController {
|
||||||
err => BadRequest.fuccess, {
|
err => BadRequest.fuccess, {
|
||||||
case (stage, level, s) =>
|
case (stage, level, s) =>
|
||||||
val score = lila.learn.StageProgress.Score(s)
|
val score = lila.learn.StageProgress.Score(s)
|
||||||
env.api.setScore(me, stage, level, score) inject Ok(Json.obj("ok" -> true))
|
env.api.setScore(me, stage, level, score) >>
|
||||||
|
Env.activity.api.addLearn(me.id, stage) inject Ok(Json.obj("ok" -> true))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,18 @@
|
||||||
package lila.activity
|
package lila.activity
|
||||||
|
|
||||||
import org.joda.time.{ DateTime, Days }
|
import org.joda.time.{ DateTime, Days }
|
||||||
import ornicar.scalalib.Zero
|
|
||||||
|
|
||||||
import lila.rating.PerfType
|
|
||||||
import lila.user.User
|
import lila.user.User
|
||||||
|
|
||||||
|
import activities._
|
||||||
|
|
||||||
case class Activity(
|
case class Activity(
|
||||||
id: Activity.Id,
|
id: Activity.Id,
|
||||||
games: Activity.Games,
|
games: Games,
|
||||||
comps: Activity.CompAnalysis,
|
comps: CompAnalysis,
|
||||||
posts: Activity.Posts,
|
posts: Posts,
|
||||||
puzzles: Activity.Puzzles
|
puzzles: Puzzles,
|
||||||
|
learn: Learn
|
||||||
) {
|
) {
|
||||||
|
|
||||||
def userId = id.userId
|
def userId = id.userId
|
||||||
|
@ -34,71 +35,12 @@ object Activity {
|
||||||
def today = Day(Days.daysBetween(genesis, DateTime.now.withTimeAtStartOfDay).getDays)
|
def today = Day(Days.daysBetween(genesis, DateTime.now.withTimeAtStartOfDay).getDays)
|
||||||
}
|
}
|
||||||
|
|
||||||
case class Rating(value: Int) extends AnyVal
|
|
||||||
case class RatingProg(before: Rating, after: Rating) {
|
|
||||||
def +(o: RatingProg) = copy(after = o.after)
|
|
||||||
}
|
|
||||||
object RatingProg {
|
|
||||||
def +(rp1O: Option[RatingProg], rp2O: Option[RatingProg]) = (rp1O, rp2O) match {
|
|
||||||
case (Some(rp1), Some(rp2)) => Some(rp1 + rp2)
|
|
||||||
case _ => rp2O orElse rp1O
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case class Games(value: Map[PerfType, Score]) extends AnyVal {
|
|
||||||
def add(pt: PerfType, score: Score) = copy(
|
|
||||||
value = value + (pt -> value.get(pt).fold(score)(_ + score))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
implicit val GamesZero = Zero.instance(Games(Map.empty))
|
|
||||||
|
|
||||||
case class Score(win: Int, loss: Int, draw: Int, rp: Option[RatingProg]) {
|
|
||||||
def +(s: Score) = copy(
|
|
||||||
win = win + s.win,
|
|
||||||
loss = loss + s.loss,
|
|
||||||
draw = draw + s.draw,
|
|
||||||
rp = RatingProg.+(rp, s.rp)
|
|
||||||
)
|
|
||||||
def size = win + loss + draw
|
|
||||||
}
|
|
||||||
object Score {
|
|
||||||
def make(res: Option[Boolean], rp: Option[RatingProg]) = Score(
|
|
||||||
win = res.has(true) ?? 1,
|
|
||||||
loss = res.has(false) ?? 1,
|
|
||||||
draw = res.isEmpty ?? 1,
|
|
||||||
rp = rp
|
|
||||||
)
|
|
||||||
}
|
|
||||||
implicit val ScoreZero = Zero.instance(Score(0, 0, 0, none))
|
|
||||||
|
|
||||||
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 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 {
|
|
||||||
def +(gameId: GameId) = CompAnalysis(gameId :: gameIds)
|
|
||||||
}
|
|
||||||
case class GameId(value: String) extends AnyVal
|
|
||||||
implicit val CompsZero = Zero.instance(CompAnalysis(Nil))
|
|
||||||
|
|
||||||
case class Puzzles(score: Score) extends AnyVal {
|
|
||||||
def +(s: Score) = Puzzles(score + s)
|
|
||||||
}
|
|
||||||
implicit val PuzzlesZero = Zero.instance(Puzzles(ScoreZero.zero))
|
|
||||||
|
|
||||||
def make(userId: User.ID) = Activity(
|
def make(userId: User.ID) = Activity(
|
||||||
id = Id today userId,
|
id = Id today userId,
|
||||||
games = GamesZero.zero,
|
games = GamesZero.zero,
|
||||||
posts = PostsZero.zero,
|
posts = PostsZero.zero,
|
||||||
comps = CompsZero.zero,
|
comps = CompsZero.zero,
|
||||||
puzzles = PuzzlesZero.zero
|
puzzles = PuzzlesZero.zero,
|
||||||
|
learn = LearnZero.zero
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,8 @@ import lila.game.Game
|
||||||
|
|
||||||
private object ActivityAggregation {
|
private object ActivityAggregation {
|
||||||
|
|
||||||
import Activity._
|
import activities._
|
||||||
|
import model._
|
||||||
|
|
||||||
def addGame(game: Game, userId: String)(a: Activity): Option[Activity] = for {
|
def addGame(game: Game, userId: String)(a: Activity): Option[Activity] = for {
|
||||||
pt <- game.perfType
|
pt <- game.perfType
|
||||||
|
|
|
@ -10,6 +10,7 @@ final class ActivityApi(coll: Coll) {
|
||||||
|
|
||||||
import Activity._
|
import Activity._
|
||||||
import BSONHandlers._
|
import BSONHandlers._
|
||||||
|
import activities._
|
||||||
|
|
||||||
def get(userId: User.ID) = coll.byId[Activity, Id](Id today userId)
|
def get(userId: User.ID) = coll.byId[Activity, Id](Id today userId)
|
||||||
|
|
||||||
|
@ -28,6 +29,9 @@ final class ActivityApi(coll: Coll) {
|
||||||
def addPuzzle(res: lila.puzzle.Puzzle.UserResult): Funit =
|
def addPuzzle(res: lila.puzzle.Puzzle.UserResult): Funit =
|
||||||
update(res.userId) { ActivityAggregation.addPuzzle(res) _ }
|
update(res.userId) { ActivityAggregation.addPuzzle(res) _ }
|
||||||
|
|
||||||
|
def addLearn(userId: User.ID, stage: String) =
|
||||||
|
update(userId) { a => a.copy(learn = a.learn + Learn.Stage(stage)).some }
|
||||||
|
|
||||||
private def getOrCreate(userId: User.ID) = get(userId) map { _ | Activity.make(userId) }
|
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 save(activity: Activity) = coll.update($id(activity.id), activity, upsert = true).void
|
||||||
private def update(userId: User.ID)(f: Activity => Option[Activity]): Funit =
|
private def update(userId: User.ID)(f: Activity => Option[Activity]): Funit =
|
||||||
|
|
|
@ -11,6 +11,8 @@ import lila.rating.PerfType
|
||||||
private object BSONHandlers {
|
private object BSONHandlers {
|
||||||
|
|
||||||
import Activity._
|
import Activity._
|
||||||
|
import activities._
|
||||||
|
import model._
|
||||||
|
|
||||||
implicit val activityIdHandler: BSONHandler[BSONString, Id] = new BSONHandler[BSONString, Id] {
|
implicit val activityIdHandler: BSONHandler[BSONString, Id] = new BSONHandler[BSONString, Id] {
|
||||||
private val sep = ':'
|
private val sep = ':'
|
||||||
|
@ -66,6 +68,10 @@ private object BSONHandlers {
|
||||||
|
|
||||||
private implicit val puzzlesHandler = Macros.handler[Puzzles]
|
private implicit val puzzlesHandler = Macros.handler[Puzzles]
|
||||||
|
|
||||||
|
private implicit val learnStageIso = Iso.string[Learn.Stage](Learn.Stage.apply, _.value)
|
||||||
|
private implicit val learnMapHandler = MapValue.MapHandler[Learn.Stage, Int]
|
||||||
|
private implicit val learnHandler = isoHandler[Learn, Map[Learn.Stage, Int], Bdoc]((l: Learn) => l.value, Learn.apply _)
|
||||||
|
|
||||||
implicit val activityHandler = new lila.db.BSON[Activity] {
|
implicit val activityHandler = new lila.db.BSON[Activity] {
|
||||||
|
|
||||||
private val id = "_id"
|
private val id = "_id"
|
||||||
|
@ -73,13 +79,15 @@ private object BSONHandlers {
|
||||||
private val comps = "c"
|
private val comps = "c"
|
||||||
private val posts = "p"
|
private val posts = "p"
|
||||||
private val puzzles = "z"
|
private val puzzles = "z"
|
||||||
|
private val learn = "l"
|
||||||
|
|
||||||
def reads(r: lila.db.BSON.Reader) = Activity(
|
def reads(r: lila.db.BSON.Reader) = Activity(
|
||||||
id = r.get[Id](id),
|
id = r.get[Id](id),
|
||||||
games = r.getD[Games](games),
|
games = r.getD[Games](games),
|
||||||
comps = r.getD[CompAnalysis](comps),
|
comps = r.getD[CompAnalysis](comps),
|
||||||
posts = r.getD[Posts](posts),
|
posts = r.getD[Posts](posts),
|
||||||
puzzles = r.getD[Puzzles](puzzles)
|
puzzles = r.getD[Puzzles](puzzles),
|
||||||
|
learn = r.getD[Learn](learn)
|
||||||
)
|
)
|
||||||
|
|
||||||
def writes(w: lila.db.BSON.Writer, o: Activity) = BSONDocument(
|
def writes(w: lila.db.BSON.Writer, o: Activity) = BSONDocument(
|
||||||
|
@ -87,7 +95,8 @@ private object BSONHandlers {
|
||||||
games -> w.zero(o.games),
|
games -> w.zero(o.games),
|
||||||
comps -> w.zero(o.comps),
|
comps -> w.zero(o.comps),
|
||||||
posts -> w.zero(o.posts),
|
posts -> w.zero(o.posts),
|
||||||
puzzles -> w.zero(o.puzzles)
|
puzzles -> w.zero(o.puzzles),
|
||||||
|
learn -> w.zero(o.learn)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
47
modules/activity/src/main/activities.scala
Normal file
47
modules/activity/src/main/activities.scala
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
package lila.activity
|
||||||
|
|
||||||
|
import ornicar.scalalib.Zero
|
||||||
|
import lila.rating.PerfType
|
||||||
|
import model._
|
||||||
|
|
||||||
|
object activities {
|
||||||
|
|
||||||
|
case class Games(value: Map[PerfType, Score]) extends AnyVal {
|
||||||
|
def add(pt: PerfType, score: Score) = copy(
|
||||||
|
value = value + (pt -> value.get(pt).fold(score)(_ + score))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
implicit val GamesZero = Zero.instance(Games(Map.empty))
|
||||||
|
|
||||||
|
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 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 {
|
||||||
|
def +(gameId: GameId) = CompAnalysis(gameId :: gameIds)
|
||||||
|
}
|
||||||
|
implicit val CompsZero = Zero.instance(CompAnalysis(Nil))
|
||||||
|
|
||||||
|
case class Puzzles(score: Score) extends AnyVal {
|
||||||
|
def +(s: Score) = Puzzles(score + s)
|
||||||
|
}
|
||||||
|
implicit val PuzzlesZero = Zero.instance(Puzzles(ScoreZero.zero))
|
||||||
|
|
||||||
|
case class Learn(value: Map[Learn.Stage, Int]) {
|
||||||
|
def +(stage: Learn.Stage) = copy(
|
||||||
|
value = value + (stage -> value.get(stage).fold(1)(1 +))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
object Learn {
|
||||||
|
case class Stage(value: String) extends AnyVal
|
||||||
|
}
|
||||||
|
implicit val LearnZero = Zero.instance(Learn(Map.empty))
|
||||||
|
}
|
38
modules/activity/src/main/model.scala
Normal file
38
modules/activity/src/main/model.scala
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
package lila.activity
|
||||||
|
|
||||||
|
import ornicar.scalalib.Zero
|
||||||
|
|
||||||
|
object model {
|
||||||
|
|
||||||
|
case class Rating(value: Int) extends AnyVal
|
||||||
|
case class RatingProg(before: Rating, after: Rating) {
|
||||||
|
def +(o: RatingProg) = copy(after = o.after)
|
||||||
|
}
|
||||||
|
object RatingProg {
|
||||||
|
def +(rp1O: Option[RatingProg], rp2O: Option[RatingProg]) = (rp1O, rp2O) match {
|
||||||
|
case (Some(rp1), Some(rp2)) => Some(rp1 + rp2)
|
||||||
|
case _ => rp2O orElse rp1O
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case class Score(win: Int, loss: Int, draw: Int, rp: Option[RatingProg]) {
|
||||||
|
def +(s: Score) = copy(
|
||||||
|
win = win + s.win,
|
||||||
|
loss = loss + s.loss,
|
||||||
|
draw = draw + s.draw,
|
||||||
|
rp = RatingProg.+(rp, s.rp)
|
||||||
|
)
|
||||||
|
def size = win + loss + draw
|
||||||
|
}
|
||||||
|
object Score {
|
||||||
|
def make(res: Option[Boolean], rp: Option[RatingProg]) = Score(
|
||||||
|
win = res.has(true) ?? 1,
|
||||||
|
loss = res.has(false) ?? 1,
|
||||||
|
draw = res.isEmpty ?? 1,
|
||||||
|
rp = rp
|
||||||
|
)
|
||||||
|
}
|
||||||
|
implicit val ScoreZero = Zero.instance(Score(0, 0, 0, none))
|
||||||
|
|
||||||
|
case class GameId(value: String) extends AnyVal
|
||||||
|
}
|
Loading…
Reference in a new issue