activity: aggregate practice progress

pull/3315/head
Thibault Duplessis 2017-07-18 23:18:12 +02:00
parent 7e14de5f7b
commit 93c121c0c8
11 changed files with 56 additions and 16 deletions

View File

@ -12,7 +12,8 @@ case class Activity(
comps: CompAnalysis,
posts: Posts,
puzzles: Puzzles,
learn: Learn
learn: Learn,
practice: Practice
) {
def userId = id.userId
@ -41,6 +42,7 @@ object Activity {
posts = PostsZero.zero,
comps = CompsZero.zero,
puzzles = PuzzlesZero.zero,
learn = LearnZero.zero
learn = LearnZero.zero,
practice = PracticeZero.zero
)
}

View File

@ -32,6 +32,9 @@ final class ActivityApi(coll: Coll) {
def addLearn(userId: User.ID, stage: String) =
update(userId) { a => a.copy(learn = a.learn + Learn.Stage(stage)).some }
def addPractice(prog: lila.practice.PracticeProgress.OnComplete) =
update(prog.userId) { a => a.copy(practice = a.practice + prog.studyId).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 =

View File

@ -7,6 +7,7 @@ import lila.db.BSON.{ MapDocument, MapValue }
import lila.db.dsl._
import lila.rating.BSONHandlers.perfTypeKeyIso
import lila.rating.PerfType
import lila.study.Study
private object BSONHandlers {
@ -72,6 +73,10 @@ private object BSONHandlers {
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 _)
private implicit val studyIdIso = Iso.string[Study.Id](Study.Id.apply, _.value)
private implicit val practiceMapHandler = MapValue.MapHandler[Study.Id, Int]
private implicit val practiceHandler = isoHandler[Practice, Map[Study.Id, Int], Bdoc]((p: Practice) => p.value, Practice.apply _)
implicit val activityHandler = new lila.db.BSON[Activity] {
private val id = "_id"
@ -80,6 +85,7 @@ private object BSONHandlers {
private val posts = "p"
private val puzzles = "z"
private val learn = "l"
private val practice = "r"
def reads(r: lila.db.BSON.Reader) = Activity(
id = r.get[Id](id),
@ -87,7 +93,8 @@ private object BSONHandlers {
comps = r.getD[CompAnalysis](comps),
posts = r.getD[Posts](posts),
puzzles = r.getD[Puzzles](puzzles),
learn = r.getD[Learn](learn)
learn = r.getD[Learn](learn),
practice = r.getD[Practice](practice)
)
def writes(w: lila.db.BSON.Writer, o: Activity) = BSONDocument(
@ -96,7 +103,8 @@ private object BSONHandlers {
comps -> w.zero(o.comps),
posts -> w.zero(o.posts),
puzzles -> w.zero(o.puzzles),
learn -> w.zero(o.learn)
learn -> w.zero(o.learn),
practice -> w.zero(o.practice)
)
}
}

View File

@ -16,14 +16,18 @@ final class Env(
coll = activityColl
)
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)
case res: lila.puzzle.Puzzle.UserResult => api addPuzzle res
}
})), 'finishGame, 'analysisReady, 'forumPost, 'finishPuzzle)
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)
case res: lila.puzzle.Puzzle.UserResult => api addPuzzle res
case prog: lila.practice.PracticeProgress.OnComplete => api addPractice prog
}
})),
'finishGame, 'analysisReady, 'forumPost, 'finishPuzzle, 'finishPractice
)
}
object Env {

View File

@ -2,6 +2,7 @@ package lila.activity
import ornicar.scalalib.Zero
import lila.rating.PerfType
import lila.study.Study
import model._
object activities {
@ -44,4 +45,11 @@ object activities {
case class Stage(value: String) extends AnyVal
}
implicit val LearnZero = Zero.instance(Learn(Map.empty))
case class Practice(value: Map[Study.Id, Int]) {
def +(studyId: Study.Id) = copy(
value = value + (studyId -> value.get(studyId).fold(1)(1 +))
)
}
implicit val PracticeZero = Zero.instance(Practice(Map.empty))
}

View File

@ -21,7 +21,8 @@ final class Env(
coll = db(CollectionProgress),
configStore = configStore[PracticeConfig]("practice", logger),
asyncCache = asyncCache,
studyApi = studyApi
studyApi = studyApi,
bus = system.lilaBus
)
system.lilaBus.subscribe(system.actorOf(Props(new Actor {

View File

@ -10,7 +10,8 @@ final class PracticeApi(
coll: Coll,
configStore: lila.memo.ConfigStore[PracticeConfig],
asyncCache: lila.memo.AsyncCache.Builder,
studyApi: lila.study.StudyApi
studyApi: lila.study.StudyApi,
bus: lila.common.Bus
) {
import BSONHandlers._
@ -82,10 +83,15 @@ final class PracticeApi(
private def save(p: PracticeProgress): Funit =
coll.update($id(p.id), p, upsert = true).void
def setNbMoves(user: User, chapterId: Chapter.Id, score: NbMoves) =
def setNbMoves(user: User, chapterId: Chapter.Id, score: NbMoves) = {
get(user) flatMap { prog =>
save(prog.withNbMoves(chapterId, score))
}
} >>- studyApi.studyIdOf(chapterId).foreach {
_ ?? { studyId =>
bus.publish(PracticeProgress.OnComplete(user.id, studyId, chapterId), 'finishPractice)
}
}
def reset(user: User) =
coll.remove($id(user.id)).void

View File

@ -2,6 +2,7 @@ package lila.practice
import org.joda.time.DateTime
import lila.user.User
import lila.study.{ Study, Chapter }
case class PracticeProgress(
@ -43,6 +44,8 @@ object PracticeProgress {
case class NbMoves(value: Int) extends AnyVal
implicit val nbMovesIso = lila.common.Iso.int[NbMoves](NbMoves.apply, _.value)
case class OnComplete(userId: User.ID, studyId: Study.Id, chapterId: Chapter.Id)
type ChapterNbMoves = Map[Chapter.Id, NbMoves]
def empty(id: Id) = PracticeProgress(

View File

@ -14,6 +14,9 @@ final class ChapterRepo(coll: Coll) {
def byId(id: Chapter.Id): Fu[Option[Chapter]] = coll.byId[Chapter, Chapter.Id](id)
def studyIdOf(chapterId: Chapter.Id): Fu[Option[Study.Id]] =
coll.primitiveOne[Study.Id]($id(chapterId), "studyId")
// def metadataById(id: Chapter.Id): Fu[Option[Chapter.Metadata]] =
// coll.find($id(id), noRootProjection).one[Chapter.Metadata]

View File

@ -77,6 +77,8 @@ final class StudyApi(
}
}
def studyIdOf = chapterRepo.studyIdOf _
def create(data: DataForm.Data, user: User): Fu[Option[Study.WithChapter]] = data.as match {
case DataForm.AsNewStudy =>
studyMaker(data, user) flatMap { res =>

View File

@ -193,7 +193,7 @@ object ApplicationBuild extends Build {
libraryDependencies ++= provided(play.api, reactivemongo.driver)
)
lazy val activity = project("activity", Seq(common, game, analyse, user, forum, study, pool, puzzle, tournament)).settings(
lazy val activity = project("activity", Seq(common, game, analyse, user, forum, study, pool, puzzle, tournament, practice)).settings(
libraryDependencies ++= provided(play.api, reactivemongo.driver)
)