refactor activities and aggregate learn stages
parent
72a582b221
commit
7e14de5f7b
|
@ -33,7 +33,8 @@ object Learn extends LilaController {
|
|||
err => BadRequest.fuccess, {
|
||||
case (stage, level, 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
|
||||
|
||||
import org.joda.time.{ DateTime, Days }
|
||||
import ornicar.scalalib.Zero
|
||||
|
||||
import lila.rating.PerfType
|
||||
import lila.user.User
|
||||
|
||||
import activities._
|
||||
|
||||
case class Activity(
|
||||
id: Activity.Id,
|
||||
games: Activity.Games,
|
||||
comps: Activity.CompAnalysis,
|
||||
posts: Activity.Posts,
|
||||
puzzles: Activity.Puzzles
|
||||
games: Games,
|
||||
comps: CompAnalysis,
|
||||
posts: Posts,
|
||||
puzzles: Puzzles,
|
||||
learn: Learn
|
||||
) {
|
||||
|
||||
def userId = id.userId
|
||||
|
@ -34,71 +35,12 @@ object Activity {
|
|||
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(
|
||||
id = Id today userId,
|
||||
games = GamesZero.zero,
|
||||
posts = PostsZero.zero,
|
||||
comps = CompsZero.zero,
|
||||
puzzles = PuzzlesZero.zero
|
||||
puzzles = PuzzlesZero.zero,
|
||||
learn = LearnZero.zero
|
||||
)
|
||||
}
|
||||
|
|
|
@ -5,7 +5,8 @@ import lila.game.Game
|
|||
|
||||
private object ActivityAggregation {
|
||||
|
||||
import Activity._
|
||||
import activities._
|
||||
import model._
|
||||
|
||||
def addGame(game: Game, userId: String)(a: Activity): Option[Activity] = for {
|
||||
pt <- game.perfType
|
||||
|
|
|
@ -10,6 +10,7 @@ final class ActivityApi(coll: Coll) {
|
|||
|
||||
import Activity._
|
||||
import BSONHandlers._
|
||||
import activities._
|
||||
|
||||
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 =
|
||||
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 save(activity: Activity) = coll.update($id(activity.id), activity, upsert = true).void
|
||||
private def update(userId: User.ID)(f: Activity => Option[Activity]): Funit =
|
||||
|
|
|
@ -11,6 +11,8 @@ import lila.rating.PerfType
|
|||
private object BSONHandlers {
|
||||
|
||||
import Activity._
|
||||
import activities._
|
||||
import model._
|
||||
|
||||
implicit val activityIdHandler: BSONHandler[BSONString, Id] = new BSONHandler[BSONString, Id] {
|
||||
private val sep = ':'
|
||||
|
@ -66,6 +68,10 @@ private object BSONHandlers {
|
|||
|
||||
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] {
|
||||
|
||||
private val id = "_id"
|
||||
|
@ -73,13 +79,15 @@ private object BSONHandlers {
|
|||
private val comps = "c"
|
||||
private val posts = "p"
|
||||
private val puzzles = "z"
|
||||
private val learn = "l"
|
||||
|
||||
def reads(r: lila.db.BSON.Reader) = Activity(
|
||||
id = r.get[Id](id),
|
||||
games = r.getD[Games](games),
|
||||
comps = r.getD[CompAnalysis](comps),
|
||||
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(
|
||||
|
@ -87,7 +95,8 @@ private object BSONHandlers {
|
|||
games -> w.zero(o.games),
|
||||
comps -> w.zero(o.comps),
|
||||
posts -> w.zero(o.posts),
|
||||
puzzles -> w.zero(o.puzzles)
|
||||
puzzles -> w.zero(o.puzzles),
|
||||
learn -> w.zero(o.learn)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
|
@ -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 New Issue