kill the strings! introduce Study.Id type and isomorphism abstractions
parent
3e87dee7d7
commit
8821c2b7ae
|
@ -24,7 +24,7 @@ object Coach extends LilaController {
|
|||
OptionFuResult(api find username) { c =>
|
||||
WithVisibleCoach(c) {
|
||||
Env.study.api.publicByIds {
|
||||
c.coach.profile.studyIds.map(_.value)
|
||||
c.coach.profile.studyIds.map(_.value).map(lila.study.Study.Id.apply)
|
||||
} flatMap Env.study.pager.withChaptersAndLiking(ctx.me) flatMap { studies =>
|
||||
api.reviews.approvedByCoach(c.coach) flatMap { reviews =>
|
||||
ctx.me.?? { api.reviews.isPending(_, c.coach) } map { isPending =>
|
||||
|
|
|
@ -7,8 +7,8 @@ import scala.concurrent.duration._
|
|||
import lila.api.Context
|
||||
import lila.app._
|
||||
import lila.common.HTTPRequest
|
||||
import lila.study.Order
|
||||
import lila.study.Study.WithChapter
|
||||
import lila.study.{ Order, Study => StudyModel }
|
||||
import views._
|
||||
|
||||
object Study extends LilaController {
|
||||
|
@ -131,7 +131,7 @@ object Study extends LilaController {
|
|||
}
|
||||
|
||||
private def chatOf(study: lila.study.Study)(implicit ctx: lila.api.Context) =
|
||||
ctx.noKid ?? Env.chat.api.userChat.findMine(study.id, ctx.me).map(some)
|
||||
ctx.noKid ?? Env.chat.api.userChat.findMine(study.id.value, ctx.me).map(some)
|
||||
|
||||
def websocket(id: String, apiVersion: Int) = SocketOption[JsValue] { implicit ctx =>
|
||||
get("sri") ?? { uid =>
|
||||
|
@ -152,7 +152,7 @@ object Study extends LilaController {
|
|||
lila.study.DataForm.form.bindFromRequest.fold(
|
||||
err => Redirect(routes.Study.byOwnerDefault(me.username)).fuccess,
|
||||
data => env.api.create(data, me) map { sc =>
|
||||
Redirect(routes.Study.show(sc.study.id))
|
||||
Redirect(routes.Study.show(sc.study.id.value))
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -165,7 +165,7 @@ object Study extends LilaController {
|
|||
def embed(id: String, chapterId: String) = Open { implicit ctx =>
|
||||
env.api.byIdWithChapter(id, chapterId) flatMap {
|
||||
_.fold(embedNotFound) {
|
||||
case lila.study.Study.WithChapter(study, chapter) => CanViewResult(study) {
|
||||
case WithChapter(study, chapter) => CanViewResult(study) {
|
||||
val setup = chapter.setup
|
||||
val pov = UserAnalysis.makePov(chapter.root.fen.value.some, setup.variant)
|
||||
Env.round.jsonView.userAnalysisJson(pov, ctx.pref, setup.orientation, owner = false) zip
|
||||
|
@ -222,7 +222,7 @@ object Study extends LilaController {
|
|||
OptionFuResult(env.api.byId(id)) { prev =>
|
||||
CanViewResult(prev) {
|
||||
env.api.clone(me, prev) map { study =>
|
||||
Redirect(routes.Study.show((study | prev).id))
|
||||
Redirect(routes.Study.show((study | prev).id.value))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -257,7 +257,7 @@ object Study extends LilaController {
|
|||
OnlyHumans {
|
||||
env.api.byIdWithChapter(id, chapterId) flatMap {
|
||||
_.fold(notFound) {
|
||||
case lila.study.Study.WithChapter(study, chapter) => CanViewResult(study) {
|
||||
case WithChapter(study, chapter) => CanViewResult(study) {
|
||||
lila.mon.export.pgn.studyChapter()
|
||||
Ok(env.pgnDump.ofChapter(study, chapter).toString).withHeaders(
|
||||
CONTENT_TYPE -> pgnContentType,
|
||||
|
@ -269,10 +269,12 @@ object Study extends LilaController {
|
|||
}
|
||||
}
|
||||
|
||||
private def CanViewResult(study: lila.study.Study)(f: => Fu[Result])(implicit ctx: lila.api.Context) =
|
||||
private def CanViewResult(study: StudyModel)(f: => Fu[Result])(implicit ctx: lila.api.Context) =
|
||||
if (canView(study)) f
|
||||
else fuccess(Unauthorized(html.study.restricted(study)))
|
||||
|
||||
private def canView(study: lila.study.Study)(implicit ctx: lila.api.Context) =
|
||||
private def canView(study: StudyModel)(implicit ctx: lila.api.Context) =
|
||||
study.isPublic || ctx.userId.exists(study.members.contains)
|
||||
|
||||
private implicit def makeStudyId(id: String): StudyModel.Id = StudyModel.Id(id)
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
title = s"Clone ${s.name}",
|
||||
icon = Some(Html("")),
|
||||
back = false) {
|
||||
<form action="@routes.Study.cloneApply(s.id)" method="POST">
|
||||
<form action="@routes.Study.cloneApply(s.id.value)" method="POST">
|
||||
<p>
|
||||
This will create a new private study with the same chapters.
|
||||
</p>
|
||||
|
@ -22,7 +22,7 @@ back = false) {
|
|||
style="margin: 30px auto; display: block; font-size: 2em;">Clone the study</button>
|
||||
</p>
|
||||
<p>
|
||||
<a href="@routes.Study.show(s.id)" class="text" data-icon="I">@trans.cancel()</a>
|
||||
<a href="@routes.Study.show(s.id.value)" class="text" data-icon="I">@trans.cancel()</a>
|
||||
</p>
|
||||
</form>
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
<div class="embedded_study analyse cg-512">@miniBoardContent</div>
|
||||
</div>
|
||||
<footer>
|
||||
@defining(routes.Study.chapter(s.id, chapter.id)) { url =>
|
||||
@defining(routes.Study.chapter(s.id.value, chapter.id)) { url =>
|
||||
<div class="left">
|
||||
<a target="_blank" href="@url"><h1>@s.name</h1></a> <em>brought to you by <a target="_blank" href="@netBaseUrl">@netDomain</a></em>
|
||||
</div>
|
||||
|
|
|
@ -17,7 +17,7 @@ explorer: {
|
|||
endpoint: "@explorerEndpoint",
|
||||
tablebaseEndpoint: "@tablebaseEndpoint"
|
||||
},
|
||||
socketUrl: "@routes.Study.websocket(s.id, apiVersion.value)",
|
||||
socketUrl: "@routes.Study.websocket(s.id.value, apiVersion.value)",
|
||||
socketVersion: @socketVersion
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
@(s: lila.study.Study.WithChaptersAndLiked)(implicit ctx: Context)
|
||||
<a class="overlay" href="@routes.Study.show(s.study.id)"></a>
|
||||
<a class="overlay" href="@routes.Study.show(s.study.id.value)"></a>
|
||||
<h2>
|
||||
<i class="icon" data-icon=""></i>
|
||||
<strong>@s.study.name</strong>
|
||||
|
|
|
@ -52,6 +52,8 @@ object Analysis {
|
|||
import lila.db.BSON
|
||||
import reactivemongo.bson._
|
||||
|
||||
type ID = String
|
||||
|
||||
private[analyse] implicit val analysisBSONHandler = new BSON[Analysis] {
|
||||
def reads(r: BSON.Reader) = {
|
||||
val startPly = r intD "ply"
|
||||
|
|
|
@ -17,7 +17,7 @@ object AnalysisRepo {
|
|||
def byId(id: ID): Fu[Option[Analysis]] = coll.byId[Analysis](id)
|
||||
|
||||
def byIds(ids: Seq[ID]): Fu[Seq[Option[Analysis]]] =
|
||||
coll.optionsByOrderedIds[Analysis](ids)(_.id)
|
||||
coll.optionsByOrderedIds[Analysis, Analysis.ID](ids)(_.id)
|
||||
|
||||
def associateToGames(games: List[Game]): Fu[List[(Game, Analysis)]] =
|
||||
byIds(games.map(_.id)) map { as =>
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
package lila.common
|
||||
|
||||
trait Iso[A, B] {
|
||||
val from: A => B
|
||||
val to: B => A
|
||||
}
|
||||
|
||||
object Iso {
|
||||
|
||||
type StringIso[B] = Iso[String, B]
|
||||
type IntIso[B] = Iso[Int, B]
|
||||
type BooleanIso[B] = Iso[Boolean, B]
|
||||
|
||||
def apply[A, B](f: A => B, t: B => A): Iso[A, B] = new Iso[A, B] {
|
||||
val from = f
|
||||
val to = t
|
||||
}
|
||||
|
||||
def string[B](from: String => B, to: B => String): StringIso[B] = apply(from, to)
|
||||
|
||||
implicit def isoIdentity[A]: Iso[A, A] = apply(identity[A] _, identity[A] _)
|
||||
|
||||
implicit val stringIsoIdentity: Iso[String, String] = isoIdentity[String]
|
||||
}
|
|
@ -28,7 +28,7 @@ trait PackageObject extends Steroids with WithFuture {
|
|||
implicit final def runOptionT[F[+_], A](ot: OptionT[F, A]): F[Option[A]] = ot.run
|
||||
|
||||
// from scalaz. We don't want to import all OptionTFunctions, because of the clash with `some`
|
||||
def optionT[M[_]] = new (({ type λ[α] = M[Option[α]] })#λ ~>({ type λ[α] = OptionT[M, α] })#λ) {
|
||||
def optionT[M[_]] = new (({ type λ[α] = M[Option[α]] })#λ ~> ({ type λ[α] = OptionT[M, α] })#λ) {
|
||||
def apply[A](a: M[Option[A]]) = new OptionT[M, A](a)
|
||||
}
|
||||
|
||||
|
|
|
@ -7,8 +7,8 @@ object PimpedJson {
|
|||
def anyValWriter[O, A: Writes](f: O => A): Writes[O] = Writes[O] { o =>
|
||||
Json toJson f(o)
|
||||
}
|
||||
def intAnyValWriter[O](f: O => Int) = anyValWriter[O, Int](f)
|
||||
def stringAnyValWriter[O](f: O => String) = anyValWriter[O, String](f)
|
||||
def intAnyValWriter[O](f: O => Int): Writes[O] = anyValWriter[O, Int](f)
|
||||
def stringAnyValWriter[O](f: O => String): Writes[O] = anyValWriter[O, String](f)
|
||||
|
||||
implicit final class LilaPimpedJsObject(val js: JsObject) extends AnyVal {
|
||||
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
package lila.db
|
||||
|
||||
import dsl._
|
||||
import org.joda.time.DateTime
|
||||
import reactivemongo.bson._
|
||||
|
||||
import dsl._
|
||||
import lila.common.Iso
|
||||
|
||||
abstract class BSON[T]
|
||||
extends BSONHandler[Bdoc, T]
|
||||
with BSONDocumentReader[T]
|
||||
|
@ -46,30 +48,30 @@ object BSON extends Handlers {
|
|||
|
||||
object MapDocument {
|
||||
|
||||
implicit def MapReader[V](implicit vr: BSONDocumentReader[V]): BSONDocumentReader[Map[String, V]] = new BSONDocumentReader[Map[String, V]] {
|
||||
def read(bson: Bdoc): Map[String, V] = {
|
||||
implicit def MapReader[K, V](implicit kIso: Iso.StringIso[K], vr: BSONDocumentReader[V]): BSONDocumentReader[Map[K, V]] = new BSONDocumentReader[Map[K, V]] {
|
||||
def read(bson: Bdoc): Map[K, V] = {
|
||||
// mutable optimized implementation
|
||||
val b = collection.immutable.Map.newBuilder[String, V]
|
||||
val b = collection.immutable.Map.newBuilder[K, V]
|
||||
for (tuple <- bson.elements)
|
||||
// assume that all values in the document are Bdocs
|
||||
b += (tuple.name -> vr.read(tuple.value.asInstanceOf[Bdoc]))
|
||||
b += (kIso.from(tuple.name) -> vr.read(tuple.value.asInstanceOf[Bdoc]))
|
||||
b.result
|
||||
}
|
||||
}
|
||||
|
||||
implicit def MapWriter[V](implicit vw: BSONDocumentWriter[V]): BSONDocumentWriter[Map[String, V]] = new BSONDocumentWriter[Map[String, V]] {
|
||||
def write(map: Map[String, V]): Bdoc = BSONDocument {
|
||||
implicit def MapWriter[K, V](implicit kIso: Iso.StringIso[K], vw: BSONDocumentWriter[V]): BSONDocumentWriter[Map[K, V]] = new BSONDocumentWriter[Map[K, V]] {
|
||||
def write(map: Map[K, V]): Bdoc = BSONDocument {
|
||||
map.toStream.map { tuple =>
|
||||
tuple._1 -> vw.write(tuple._2)
|
||||
kIso.to(tuple._1) -> vw.write(tuple._2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
implicit def MapHandler[V](implicit vr: BSONDocumentReader[V], vw: BSONDocumentWriter[V]): BSONHandler[Bdoc, Map[String, V]] = new BSONHandler[Bdoc, Map[String, V]] {
|
||||
private val reader = MapReader[V]
|
||||
private val writer = MapWriter[V]
|
||||
def read(bson: Bdoc): Map[String, V] = reader read bson
|
||||
def write(map: Map[String, V]): Bdoc = writer write map
|
||||
implicit def MapHandler[K, V](implicit kIso: Iso.StringIso[K], vr: BSONDocumentReader[V], vw: BSONDocumentWriter[V]): BSONHandler[Bdoc, Map[K, V]] = new BSONHandler[Bdoc, Map[K, V]] {
|
||||
private val reader = MapReader[K, V]
|
||||
private val writer = MapWriter[K, V]
|
||||
def read(bson: Bdoc): Map[K, V] = reader read bson
|
||||
def write(map: Map[K, V]): Bdoc = writer write map
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ trait CollExt { self: dsl with QueryBuilderExt =>
|
|||
|
||||
def exists(selector: Bdoc): Fu[Boolean] = countSel(selector).map(0!=)
|
||||
|
||||
def byOrderedIds[D: BSONDocumentReader](ids: Iterable[String], readPreference: ReadPreference = ReadPreference.primary)(docId: D => String): Fu[List[D]] =
|
||||
def byOrderedIds[D: BSONDocumentReader, I: BSONValueWriter](ids: Iterable[I], readPreference: ReadPreference = ReadPreference.primary)(docId: D => I): Fu[List[D]] =
|
||||
coll.find($inIds(ids)).cursor[D](readPreference = readPreference).
|
||||
collect[List](Int.MaxValue, err = Cursor.FailOnError[List[D]]()).
|
||||
map { docs =>
|
||||
|
@ -45,8 +45,8 @@ trait CollExt { self: dsl with QueryBuilderExt =>
|
|||
// def byOrderedIds[A <: Identified[String]: TubeInColl](ids: Iterable[String]): Fu[List[A]] =
|
||||
// byOrderedIds[String, A](ids)
|
||||
|
||||
def optionsByOrderedIds[D: BSONDocumentReader](ids: Iterable[String])(docId: D => String): Fu[List[Option[D]]] =
|
||||
byIds[D](ids) map { docs =>
|
||||
def optionsByOrderedIds[D: BSONDocumentReader, I: BSONValueWriter](ids: Iterable[I])(docId: D => I): Fu[List[Option[D]]] =
|
||||
byIds[D, I](ids) map { docs =>
|
||||
val docsMap = docs.map(u => docId(u) -> u).toMap
|
||||
ids.map(docsMap.get).toList
|
||||
}
|
||||
|
|
|
@ -4,6 +4,9 @@ import org.joda.time.DateTime
|
|||
import reactivemongo.bson._
|
||||
import scalaz.NonEmptyList
|
||||
|
||||
import lila.common.Iso
|
||||
import lila.common.Iso._
|
||||
|
||||
trait Handlers {
|
||||
|
||||
implicit object BSONJodaDateTimeHandler extends BSONHandler[BSONDateTime, DateTime] {
|
||||
|
@ -11,26 +14,23 @@ trait Handlers {
|
|||
def write(x: DateTime) = BSONDateTime(x.getMillis)
|
||||
}
|
||||
|
||||
def stringAnyValHandler[A](from: A => String, to: String => A) = new BSONHandler[BSONString, A] {
|
||||
def read(x: BSONString) = to(x.value)
|
||||
def write(x: A) = BSONString(from(x))
|
||||
}
|
||||
def intAnyValHandler[A](from: A => Int, to: Int => A) = new BSONHandler[BSONInteger, A] {
|
||||
def read(x: BSONInteger) = to(x.value)
|
||||
def write(x: A) = BSONInteger(from(x))
|
||||
}
|
||||
def booleanAnyValHandler[A](from: A => Boolean, to: Boolean => A) = new BSONHandler[BSONBoolean, A] {
|
||||
def read(x: BSONBoolean) = to(x.value)
|
||||
def write(x: A) = BSONBoolean(from(x))
|
||||
}
|
||||
def dateAnyValHandler[A](from: A => DateTime, to: DateTime => A) = new BSONHandler[BSONDateTime, A] {
|
||||
def read(x: BSONDateTime) = to(BSONJodaDateTimeHandler read x)
|
||||
def write(x: A) = BSONJodaDateTimeHandler write from(x)
|
||||
}
|
||||
def isoHandler[A, B, C <: BSONValue](from: A => B, to: B => A)(implicit handler: BSONHandler[C, B]) = new BSONHandler[C, A] {
|
||||
def read(x: C): A = to(handler read x)
|
||||
def write(x: A): C = handler write from(x)
|
||||
def isoHandler[A, B, C <: BSONValue](iso: Iso[B, A])(implicit handler: BSONHandler[C, B]): BSONHandler[C, A] = new BSONHandler[C, A] {
|
||||
def read(x: C): A = iso.from(handler read x)
|
||||
def write(x: A): C = handler write iso.to(x)
|
||||
}
|
||||
def isoHandler[A, B, C <: BSONValue](to: A => B, from: B => A)(implicit handler: BSONHandler[C, B]): BSONHandler[C, A] =
|
||||
isoHandler(Iso(from, to))
|
||||
|
||||
def stringIsoHandler[A](implicit iso: StringIso[A]): BSONHandler[BSONString, A] = isoHandler[A, String, BSONString](iso)
|
||||
def stringAnyValHandler[A](to: A => String, from: String => A): BSONHandler[BSONString, A] = stringIsoHandler(Iso(from, to))
|
||||
|
||||
def intIsoHandler[A](implicit iso: IntIso[A]): BSONHandler[BSONInteger, A] = isoHandler[A, Int, BSONInteger](iso)
|
||||
def intAnyValHandler[A](to: A => Int, from: Int => A): BSONHandler[BSONInteger, A] = intIsoHandler(Iso(from, to))
|
||||
|
||||
def booleanIsoHandler[A](implicit iso: BooleanIso[A]): BSONHandler[BSONBoolean, A] = isoHandler[A, Boolean, BSONBoolean](iso)
|
||||
def booleanAnyValHandler[A](to: A => Boolean, from: Boolean => A): BSONHandler[BSONBoolean, A] = booleanIsoHandler(Iso(from, to))
|
||||
|
||||
def dateIsoHandler[A](implicit iso: Iso[DateTime, A]): BSONHandler[BSONDateTime, A] = isoHandler[A, DateTime, BSONDateTime](iso)
|
||||
|
||||
implicit def bsonArrayToListHandler[T](implicit reader: BSONReader[_ <: BSONValue, T], writer: BSONWriter[T, _ <: BSONValue]): BSONHandler[BSONArray, List[T]] = new BSONHandler[BSONArray, List[T]] {
|
||||
def read(array: BSONArray) = readStream(array, reader.asInstanceOf[BSONReader[BSONValue, T]]).toList
|
||||
|
|
|
@ -62,6 +62,8 @@ case class Post(
|
|||
|
||||
object Post {
|
||||
|
||||
type ID = String
|
||||
|
||||
val idSize = 8
|
||||
|
||||
def make(
|
||||
|
|
|
@ -121,8 +121,8 @@ final class PostApi(
|
|||
} yield PostView(post, topic, categ, lastPageOf(topic))
|
||||
} flatten
|
||||
|
||||
def viewsFromIds(postIds: Seq[String]): Fu[List[PostView]] =
|
||||
env.postColl.byOrderedIds[Post](postIds)(_.id) flatMap views
|
||||
def viewsFromIds(postIds: Seq[Post.ID]): Fu[List[PostView]] =
|
||||
env.postColl.byOrderedIds[Post, Post.ID](postIds)(_.id) flatMap views
|
||||
|
||||
def view(post: Post): Fu[Option[PostView]] =
|
||||
views(List(post)) map (_.headOption)
|
||||
|
|
|
@ -26,13 +26,13 @@ object GameRepo {
|
|||
|
||||
def game(gameId: ID): Fu[Option[Game]] = coll.byId[Game](gameId)
|
||||
|
||||
def gamesFromPrimary(gameIds: Seq[ID]): Fu[List[Game]] = coll.byOrderedIds[Game](gameIds)(_.id)
|
||||
def gamesFromPrimary(gameIds: Seq[ID]): Fu[List[Game]] = coll.byOrderedIds[Game, Game.ID](gameIds)(_.id)
|
||||
|
||||
def gamesFromSecondary(gameIds: Seq[ID]): Fu[List[Game]] =
|
||||
coll.byOrderedIds[Game](gameIds, readPreference = ReadPreference.secondaryPreferred)(_.id)
|
||||
coll.byOrderedIds[Game, Game.ID](gameIds, readPreference = ReadPreference.secondaryPreferred)(_.id)
|
||||
|
||||
def gameOptions(gameIds: Seq[ID]): Fu[Seq[Option[Game]]] =
|
||||
coll.optionsByOrderedIds[Game](gameIds)(_.id)
|
||||
coll.optionsByOrderedIds[Game, Game.ID](gameIds)(_.id)
|
||||
|
||||
def finished(gameId: ID): Fu[Option[Game]] =
|
||||
coll.uno[Game]($id(gameId) ++ Query.finished)
|
||||
|
@ -68,7 +68,7 @@ object GameRepo {
|
|||
def remove(id: ID) = coll.remove($id(id)).void
|
||||
|
||||
def userPovsByGameIds(gameIds: List[String], user: User): Fu[List[Pov]] =
|
||||
coll.byOrderedIds[Game](gameIds)(_.id) map { _.flatMap(g => Pov(g, user)) }
|
||||
coll.byOrderedIds[Game, Game.ID](gameIds)(_.id) map { _.flatMap(g => Pov(g, user)) }
|
||||
|
||||
def recentPovsByUser(user: User, nb: Int): Fu[List[Pov]] =
|
||||
coll.find(Query user user).sort(Query.sortCreated).cursor[Game]().gather[List](nb)
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
package lila.learn
|
||||
|
||||
import reactivemongo.bson._
|
||||
|
||||
import lila.common.Iso
|
||||
import lila.db.BSON
|
||||
import lila.db.dsl._
|
||||
import reactivemongo.bson._
|
||||
|
||||
object BSONHandlers {
|
||||
|
||||
|
@ -11,7 +13,8 @@ object BSONHandlers {
|
|||
private implicit val ScoreHandler = intAnyValHandler[Score](_.value, Score.apply)
|
||||
private implicit val ScoresHandler = bsonArrayToVectorHandler[Score]
|
||||
private implicit val StageProgressHandler =
|
||||
isoHandler[StageProgress, Vector[Score], BSONArray](_.scores, StageProgress.apply)(ScoresHandler)
|
||||
isoHandler[StageProgress, Vector[Score], BSONArray](
|
||||
(s: StageProgress) => s.scores, StageProgress.apply _)(ScoresHandler)
|
||||
|
||||
implicit val LearnProgressIdHandler = new BSONHandler[BSONString, LearnProgress.Id] {
|
||||
def read(bs: BSONString): LearnProgress.Id =
|
||||
|
|
|
@ -19,13 +19,13 @@ case class LearnProgress(
|
|||
|
||||
object LearnProgress {
|
||||
|
||||
sealed trait Id {
|
||||
sealed trait Id extends Any {
|
||||
def str: String
|
||||
}
|
||||
case class UserId(value: String) extends Id {
|
||||
case class UserId(value: String) extends AnyVal with Id {
|
||||
def str = value
|
||||
}
|
||||
case class AnonId(value: String) extends Id {
|
||||
case class AnonId(value: String) extends AnyVal with Id {
|
||||
def str = s"anon:$value"
|
||||
}
|
||||
|
||||
|
|
|
@ -2,23 +2,32 @@ package lila.practice
|
|||
|
||||
import lila.db.BSON
|
||||
import lila.db.dsl._
|
||||
import lila.study.Study
|
||||
import reactivemongo.bson._
|
||||
|
||||
object BSONHandlers {
|
||||
|
||||
import lila.study.BSONHandlers.{ StudyIdBSONHandler }
|
||||
import StudyProgress.NbMoves
|
||||
|
||||
private implicit val NbMovesHandler = intAnyValHandler[NbMoves](_.value, NbMoves.apply)
|
||||
private implicit val ChapterNbMovesHandler = BSON.MapValue.MapHandler[NbMoves]
|
||||
private implicit val StudyProgressHandler =
|
||||
isoHandler[StudyProgress, StudyProgress.ChapterNbMoves, BSONDocument](_.moves, StudyProgress.apply)(ChapterNbMovesHandler)
|
||||
// private implicit val nbMovesHandler = intAnyValHandler[NbMoves](_.value, NbMoves.apply)
|
||||
// private implicit val chapterNbMovesHandler = BSON.MapValue.MapHandler[NbMoves]
|
||||
// private implicit val studyProgressHandler: BSONHandler[Bdoc, StudyProgress] =
|
||||
// isoHandler[StudyProgress, StudyProgress.ChapterNbMoves, BSONDocument]((s: StudyProgress) => s.moves, StudyProgress.apply _)(ChapterNbMovesHandler)
|
||||
|
||||
implicit val PracticeProgressIdHandler = new BSONHandler[BSONString, PracticeProgress.Id] {
|
||||
def read(bs: BSONString): PracticeProgress.Id =
|
||||
if (bs.value startsWith "anon:") PracticeProgress.AnonId(bs.value drop 5)
|
||||
else PracticeProgress.UserId(bs.value)
|
||||
def write(x: PracticeProgress.Id) = BSONString(x.str)
|
||||
}
|
||||
private implicit val PracticeProgressStudiesHandler = BSON.MapValue.MapHandler[StudyProgress]
|
||||
implicit val PracticeProgressHandler = Macros.handler[PracticeProgress]
|
||||
// implicit val practiceProgressIdHandler = new BSONHandler[BSONString, PracticeProgress.Id] {
|
||||
// def read(bs: BSONString): PracticeProgress.Id =
|
||||
// if (bs.value startsWith "anon:") PracticeProgress.AnonId(bs.value drop 5)
|
||||
// else PracticeProgress.UserId(bs.value)
|
||||
// def write(x: PracticeProgress.Id) = BSONString(x.str)
|
||||
// }
|
||||
|
||||
// import Study.idIso
|
||||
// private implicit val practiceProgressStudiesHandler =
|
||||
// BSON.MapDocument.MapHandler[Study.Id, StudyProgress](
|
||||
// idIso,
|
||||
// studyProgressHandler,
|
||||
// studyProgressHandler)
|
||||
|
||||
// implicit val practiceProgressHandler = Macros.handler[PracticeProgress]
|
||||
}
|
||||
|
|
|
@ -7,17 +7,17 @@ final class PracticeApi(coll: Coll) {
|
|||
|
||||
import BSONHandlers._
|
||||
|
||||
def get(user: User): Fu[PracticeProgress] =
|
||||
coll.uno[PracticeProgress]($id(user.id)) map { _ | PracticeProgress.empty(PracticeProgress.UserId(user.id)) }
|
||||
// def get(user: User): Fu[PracticeProgress] =
|
||||
// coll.uno[PracticeProgress]($id(user.id)) map { _ | PracticeProgress.empty(PracticeProgress.UserId(user.id)) }
|
||||
|
||||
private def save(p: PracticeProgress): Funit =
|
||||
coll.update($id(p.id), p, upsert = true).void
|
||||
// private def save(p: PracticeProgress): Funit =
|
||||
// coll.update($id(p.id), p, upsert = true).void
|
||||
|
||||
def setScore(user: User, stage: String, level: Int, score: StageProgress.Score) =
|
||||
get(user) flatMap { prog =>
|
||||
save(prog.withScore(stage, level, score))
|
||||
}
|
||||
// def setScore(user: User, stage: String, level: Int, score: StageProgress.Score) =
|
||||
// get(user) flatMap { prog =>
|
||||
// save(prog.withScore(stage, level, score))
|
||||
// }
|
||||
|
||||
def reset(user: User) =
|
||||
coll.remove($id(user.id)).void
|
||||
// def reset(user: User) =
|
||||
// coll.remove($id(user.id)).void
|
||||
}
|
||||
|
|
|
@ -4,28 +4,28 @@ import org.joda.time.DateTime
|
|||
|
||||
case class PracticeProgress(
|
||||
_id: PracticeProgress.Id,
|
||||
studies: Map[lila.study.Study.ID, StudyProgress],
|
||||
studies: Map[lila.study.Study.Id, StudyProgress],
|
||||
createdAt: DateTime,
|
||||
updatedAt: DateTime) {
|
||||
|
||||
def id = _id
|
||||
|
||||
def withNbMoves(chapterId: , level: Int, s: StageProgress.Score) = copy(
|
||||
studies = stages + (
|
||||
stage -> stages.getOrElse(stage, StageProgress empty stage).withScore(level, s)
|
||||
),
|
||||
updatedAt = DateTime.now)
|
||||
// def withNbMoves(chapterId: String, level: Int, s: StageProgress.Score) = copy(
|
||||
// studies = stages + (
|
||||
// stage -> stages.getOrElse(stage, StageProgress empty stage).withScore(level, s)
|
||||
// ),
|
||||
// updatedAt = DateTime.now)
|
||||
}
|
||||
|
||||
object PracticeProgress {
|
||||
|
||||
sealed trait Id extends AnyVal {
|
||||
sealed trait Id extends Any {
|
||||
def str: String
|
||||
}
|
||||
case class UserId(value: String) extends Id {
|
||||
case class UserId(value: String) extends AnyVal with Id {
|
||||
def str = value
|
||||
}
|
||||
case class AnonId(value: String) extends Id {
|
||||
case class AnonId(value: String) extends AnyVal with Id {
|
||||
def str = s"anon:$value"
|
||||
}
|
||||
|
||||
|
|
|
@ -4,10 +4,10 @@ case class StudyProgress(moves: StudyProgress.ChapterNbMoves) extends AnyVal {
|
|||
|
||||
import StudyProgress._
|
||||
|
||||
def withScore(level: Int, s: Score) = copy(
|
||||
scores = (0 until scores.size.max(level)).map { i =>
|
||||
scores.lift(i) | Score(0)
|
||||
}.updated(level - 1, s).toVector)
|
||||
// def withScore(level: Int, s: Score) = copy(
|
||||
// scores = (0 until scores.size.max(level)).map { i =>
|
||||
// scores.lift(i) | Score(0)
|
||||
// }.updated(level - 1, s).toVector)
|
||||
}
|
||||
|
||||
object StudyProgress {
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
package lila.search
|
||||
|
||||
case class Index(name: String)
|
||||
case class Index(name: String) extends AnyVal
|
||||
|
||||
case class Id(value: String)
|
||||
case class Id(value: String) extends AnyVal
|
||||
|
||||
case class StringQuery(value: String)
|
||||
case class From(value: Int)
|
||||
case class Size(value: Int)
|
||||
case class StringQuery(value: String) extends AnyVal
|
||||
case class From(value: Int) extends AnyVal
|
||||
case class Size(value: Int) extends AnyVal
|
||||
|
||||
case class SearchResponse(ids: List[String])
|
||||
case class SearchResponse(ids: List[String]) extends AnyVal
|
||||
|
||||
object SearchResponse {
|
||||
def apply(txt: String): SearchResponse = SearchResponse(txt.split(',').toList)
|
||||
}
|
||||
|
||||
case class CountResponse(count: Int)
|
||||
case class CountResponse(count: Int) extends AnyVal
|
||||
|
||||
object CountResponse {
|
||||
def apply(txt: String): CountResponse = CountResponse(~parseIntOption(txt))
|
||||
|
|
|
@ -12,10 +12,15 @@ import lila.db.BSON.{ Reader, Writer }
|
|||
import lila.db.dsl._
|
||||
import lila.tree.Node.{ Shape, Shapes }
|
||||
|
||||
private object BSONHandlers {
|
||||
import lila.common.Iso
|
||||
import lila.common.Iso._
|
||||
|
||||
object BSONHandlers {
|
||||
|
||||
import Chapter._
|
||||
|
||||
implicit val StudyIdBSONHandler = stringIsoHandler[Study.Id](Study.idIso)
|
||||
|
||||
private implicit val PosBSONHandler = new BSONHandler[BSONString, Pos] {
|
||||
def read(bsonStr: BSONString): Pos = Pos.posAt(bsonStr.value) err s"No such pos: ${bsonStr.value}"
|
||||
def write(x: Pos) = BSONString(x.key)
|
||||
|
@ -110,10 +115,10 @@ private object BSONHandlers {
|
|||
"b" -> w.strO(writePocket(s.pockets.black)))
|
||||
}
|
||||
|
||||
private implicit val GlyphsBSONHandler = new BSONHandler[BSONArray, Glyphs] {
|
||||
private implicit val GlyphsBSONHandler = new BSONHandler[Barr, Glyphs] {
|
||||
private val idsHandler = bsonArrayToListHandler[Int]
|
||||
def read(b: BSONArray) = Glyphs.fromList(idsHandler read b flatMap Glyph.find)
|
||||
def write(x: Glyphs) = BSONArray(x.toList.map(_.id).map(BSONInteger.apply))
|
||||
def read(b: Barr) = Glyphs.fromList(idsHandler read b flatMap Glyph.find)
|
||||
def write(x: Glyphs) = $arr(x.toList.map(_.id).map(BSONInteger.apply))
|
||||
}
|
||||
|
||||
private implicit def NodeBSONHandler: BSON[Node] = new BSON[Node] {
|
||||
|
@ -162,9 +167,9 @@ private object BSONHandlers {
|
|||
"z" -> s.crazyData,
|
||||
"n" -> s.children)
|
||||
}
|
||||
implicit val ChildrenBSONHandler = new BSONHandler[BSONArray, Node.Children] {
|
||||
implicit val ChildrenBSONHandler = new BSONHandler[Barr, Node.Children] {
|
||||
private val nodesHandler = bsonArrayToVectorHandler[Node]
|
||||
def read(b: BSONArray) = try {
|
||||
def read(b: Barr) = try {
|
||||
Node.Children(nodesHandler read b)
|
||||
}
|
||||
catch {
|
||||
|
@ -204,7 +209,7 @@ private object BSONHandlers {
|
|||
implicit val ChapterBSONHandler = Macros.handler[Chapter]
|
||||
implicit val ChapterMetadataBSONHandler = Macros.handler[Chapter.Metadata]
|
||||
|
||||
private implicit val ChaptersMap = BSON.MapDocument.MapHandler[Chapter]
|
||||
private implicit val ChaptersMap = BSON.MapDocument.MapHandler[Chapter.ID, Chapter]
|
||||
|
||||
implicit val PositionRefBSONHandler = new BSONHandler[BSONString, Position.Ref] {
|
||||
def read(b: BSONString) = Position.Ref.decode(b.value) err s"Invalid position ${b.value}"
|
||||
|
@ -220,7 +225,7 @@ private object BSONHandlers {
|
|||
def write(x: StudyMember) = DbMemberBSONHandler write DbMember(x.role, x.addedAt)
|
||||
}
|
||||
private[study] implicit val MembersBSONHandler = new BSONHandler[Bdoc, StudyMembers] {
|
||||
private val mapHandler = BSON.MapDocument.MapHandler[DbMember]
|
||||
private val mapHandler = BSON.MapDocument.MapHandler[String, DbMember]
|
||||
def read(b: Bdoc) = StudyMembers(mapHandler read b map {
|
||||
case (id, dbMember) => id -> StudyMember(id, dbMember.role, dbMember.addedAt)
|
||||
})
|
||||
|
@ -236,7 +241,7 @@ private object BSONHandlers {
|
|||
def read(bs: BSONString) = bs.value.split(' ') match {
|
||||
case Array("scratch") => From.Scratch
|
||||
case Array("game", id) => From.Game(id)
|
||||
case Array("study", id) => From.Study(id)
|
||||
case Array("study", id) => From.Study(Study.Id(id))
|
||||
case _ => sys error s"Invalid from ${bs.value}"
|
||||
}
|
||||
def write(x: From) = BSONString(x match {
|
||||
|
@ -262,7 +267,7 @@ private object BSONHandlers {
|
|||
import Study.Likes
|
||||
private[study] implicit val LikesBSONHandler = intAnyValHandler[Likes](_.value, Likes.apply)
|
||||
import Study.Rank
|
||||
private[study] implicit val RankBSONHandler = dateAnyValHandler[Rank](_.value, Rank.apply)
|
||||
private[study] implicit val RankBSONHandler = dateIsoHandler[Rank](Iso[DateTime, Rank](Rank.apply, _.value))
|
||||
|
||||
implicit val StudyBSONHandler = Macros.handler[Study]
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ import lila.user.User
|
|||
|
||||
case class Chapter(
|
||||
_id: Chapter.ID,
|
||||
studyId: Study.ID,
|
||||
studyId: Study.Id,
|
||||
name: String,
|
||||
setup: Chapter.Setup,
|
||||
root: Node.Root,
|
||||
|
@ -96,7 +96,7 @@ object Chapter {
|
|||
def compare(that: Ply) = value - that.value
|
||||
}
|
||||
|
||||
case class FullId(studyId: Study.ID, chapterId: Chapter.ID)
|
||||
case class FullId(studyId: Study.Id, chapterId: Chapter.ID)
|
||||
|
||||
private val defaultNamePattern = """^Chapter \d+$""".r.pattern
|
||||
def isDefaultName(str: String) = defaultNamePattern.matcher(str).matches
|
||||
|
@ -107,7 +107,7 @@ object Chapter {
|
|||
|
||||
def makeId = scala.util.Random.alphanumeric take idSize mkString
|
||||
|
||||
def make(studyId: Study.ID, name: String, setup: Setup, root: Node.Root, tags: List[Tag], order: Int, ownerId: User.ID, practice: Boolean, conceal: Option[Ply]) = Chapter(
|
||||
def make(studyId: Study.Id, name: String, setup: Setup, root: Node.Root, tags: List[Tag], order: Int, ownerId: User.ID, practice: Boolean, conceal: Option[Ply]) = Chapter(
|
||||
_id = makeId,
|
||||
studyId = studyId,
|
||||
name = toName(name),
|
||||
|
|
|
@ -19,20 +19,20 @@ final class ChapterRepo(coll: Coll) {
|
|||
|
||||
def deleteByStudy(s: Study): Funit = coll.remove($studyId(s.id)).void
|
||||
|
||||
def byIdAndStudy(id: Chapter.ID, studyId: Study.ID): Fu[Option[Chapter]] =
|
||||
def byIdAndStudy(id: Chapter.ID, studyId: Study.Id): Fu[Option[Chapter]] =
|
||||
coll.byId[Chapter](id).map { _.filter(_.studyId == studyId) }
|
||||
|
||||
def firstByStudy(studyId: Study.ID): Fu[Option[Chapter]] =
|
||||
def firstByStudy(studyId: Study.Id): Fu[Option[Chapter]] =
|
||||
coll.find($studyId(studyId)).sort($sort asc "order").one[Chapter]
|
||||
|
||||
def orderedMetadataByStudy(studyId: Study.ID): Fu[List[Chapter.Metadata]] =
|
||||
def orderedMetadataByStudy(studyId: Study.Id): Fu[List[Chapter.Metadata]] =
|
||||
coll.find(
|
||||
$studyId(studyId),
|
||||
noRootProjection
|
||||
).sort($sort asc "order").list[Chapter.Metadata](maxChapters)
|
||||
|
||||
// loads all study chapters in memory! only used for search indexing and cloning
|
||||
def orderedByStudy(studyId: Study.ID): Fu[List[Chapter]] =
|
||||
def orderedByStudy(studyId: Study.Id): Fu[List[Chapter]] =
|
||||
coll.find($studyId(studyId))
|
||||
.sort($sort asc "order")
|
||||
.cursor[Chapter](readPreference = ReadPreference.secondaryPreferred)
|
||||
|
@ -43,7 +43,7 @@ final class ChapterRepo(coll: Coll) {
|
|||
coll.updateField($studyId(study.id) ++ $id(id), "order", index + 1)
|
||||
}.sequenceFu.void
|
||||
|
||||
def nextOrderByStudy(studyId: Study.ID): Fu[Int] =
|
||||
def nextOrderByStudy(studyId: Study.Id): Fu[Int] =
|
||||
coll.primitiveOne[Int](
|
||||
$studyId(studyId),
|
||||
$sort desc "order",
|
||||
|
@ -59,15 +59,15 @@ final class ChapterRepo(coll: Coll) {
|
|||
def setTagsFor(chapter: Chapter) =
|
||||
coll.updateField($id(chapter.id), "tags", chapter.tags).void
|
||||
|
||||
private[study] def namesByStudyIds(studyIds: Seq[Study.ID]): Fu[Map[Study.ID, Vector[String]]] =
|
||||
private[study] def namesByStudyIds(studyIds: Seq[Study.Id]): Fu[Map[Study.Id, Vector[String]]] =
|
||||
coll.find(
|
||||
$doc("studyId" $in studyIds),
|
||||
$doc("studyId" -> true, "name" -> true)
|
||||
).sort($sort asc "order").list[Bdoc]().map { docs =>
|
||||
docs.foldLeft(Map.empty[Study.ID, Vector[String]]) {
|
||||
docs.foldLeft(Map.empty[Study.Id, Vector[String]]) {
|
||||
case (hash, doc) => {
|
||||
for {
|
||||
studyId <- doc.getAs[String]("studyId")
|
||||
studyId <- doc.getAs[Study.Id]("studyId")
|
||||
name <- doc.getAs[String]("name")
|
||||
} yield hash + (studyId -> (hash.get(studyId) match {
|
||||
case None => Vector(name)
|
||||
|
@ -77,7 +77,7 @@ final class ChapterRepo(coll: Coll) {
|
|||
}
|
||||
}
|
||||
|
||||
def countByStudyId(studyId: Study.ID): Fu[Int] =
|
||||
def countByStudyId(studyId: Study.Id): Fu[Int] =
|
||||
coll.countSel($studyId(studyId))
|
||||
|
||||
def insert(s: Chapter): Funit = coll.insert(s).void
|
||||
|
@ -87,5 +87,5 @@ final class ChapterRepo(coll: Coll) {
|
|||
def delete(id: Chapter.ID): Funit = coll.remove($id(id)).void
|
||||
def delete(c: Chapter): Funit = delete(c.id)
|
||||
|
||||
private def $studyId(id: Study.ID) = $doc("studyId" -> id)
|
||||
private def $studyId(id: Study.Id) = $doc("studyId" -> id)
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ final class Env(
|
|||
private val socketHub = system.actorOf(
|
||||
Props(new lila.socket.SocketHubActor.Default[Socket] {
|
||||
def mkActor(studyId: String) = new Socket(
|
||||
studyId = studyId,
|
||||
studyId = Study.Id(studyId),
|
||||
jsonView = jsonView,
|
||||
studyRepo = studyRepo,
|
||||
lightUser = getLightUser,
|
||||
|
@ -47,8 +47,8 @@ final class Env(
|
|||
socketTimeout = SocketTimeout)
|
||||
}), name = SocketName)
|
||||
|
||||
def version(studyId: Study.ID): Fu[Int] =
|
||||
socketHub ? Ask(studyId, GetVersion) mapTo manifest[Int]
|
||||
def version(studyId: Study.Id): Fu[Int] =
|
||||
socketHub ? Ask(studyId.value, GetVersion) mapTo manifest[Int]
|
||||
|
||||
lazy val socketHandler = new SocketHandler(
|
||||
hub = hub,
|
||||
|
|
|
@ -93,6 +93,8 @@ object JsonView {
|
|||
|
||||
case class JsData(study: JsObject, analysis: JsObject)
|
||||
|
||||
implicit val studyIdWrites: Writes[Study.Id] = stringAnyValWriter[Study.Id](_.value)
|
||||
|
||||
private implicit val uciWrites: Writes[Uci] = Writes[Uci] { u =>
|
||||
JsString(u.uci)
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ final class PgnDump(
|
|||
s"lichess_study_${slugify(study.name)}_${slugify(chapter.name)}_by_${ownerName(study)}_${date}.pgn", "")
|
||||
}
|
||||
|
||||
private def studyUrl(id: String) = s"$netBaseUrl/study/$id"
|
||||
private def studyUrl(id: Study.Id) = s"$netBaseUrl/study/$id"
|
||||
|
||||
private val dateFormat = DateTimeFormat forPattern "yyyy.MM.dd";
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ import lila.tree.Node.{ Shapes, Comment }
|
|||
import lila.user.User
|
||||
|
||||
private final class Socket(
|
||||
studyId: String,
|
||||
studyId: Study.Id,
|
||||
jsonView: JsonView,
|
||||
studyRepo: StudyRepo,
|
||||
lightUser: lila.common.LightUser.Getter,
|
||||
|
|
|
@ -37,7 +37,7 @@ private[study] final class SocketHandler(
|
|||
|
||||
private def controller(
|
||||
socket: ActorRef,
|
||||
studyId: Study.ID,
|
||||
studyId: Study.Id,
|
||||
uid: Uid,
|
||||
member: Socket.Member,
|
||||
owner: Boolean): Handler.Controller = ({
|
||||
|
@ -234,7 +234,7 @@ private[study] final class SocketHandler(
|
|||
v <- (o \ "d" \ "liked").asOpt[Boolean]
|
||||
} api.like(studyId, byUserId, v, socket, uid)
|
||||
}: Handler.Controller) orElse lila.chat.Socket.in(
|
||||
chatId = studyId,
|
||||
chatId = studyId.value,
|
||||
member = member,
|
||||
socket = socket,
|
||||
chat = chat)
|
||||
|
@ -254,11 +254,11 @@ private[study] final class SocketHandler(
|
|||
private implicit val setTagReader = Json.reads[actorApi.SetTag]
|
||||
|
||||
def join(
|
||||
studyId: Study.ID,
|
||||
studyId: Study.Id,
|
||||
uid: Uid,
|
||||
user: Option[User],
|
||||
owner: Boolean): Fu[Option[JsSocketHandler]] = for {
|
||||
socket ← socketHub ? Get(studyId) mapTo manifest[ActorRef]
|
||||
socket ← socketHub ? Get(studyId.value) mapTo manifest[ActorRef]
|
||||
join = Socket.Join(uid = uid, userId = user.map(_.id), troll = user.??(_.troll), owner = owner)
|
||||
handler ← Handler(hub, socket, uid.value, join) {
|
||||
case Socket.Connected(enum, member) =>
|
||||
|
|
|
@ -5,7 +5,7 @@ import org.joda.time.DateTime
|
|||
import lila.user.User
|
||||
|
||||
case class Study(
|
||||
_id: Study.ID,
|
||||
_id: Study.Id,
|
||||
name: String,
|
||||
members: StudyMembers,
|
||||
position: Position.Ref,
|
||||
|
@ -65,6 +65,9 @@ case class Study(
|
|||
|
||||
object Study {
|
||||
|
||||
case class Id(value: String) extends AnyVal with StringValue
|
||||
implicit val idIso = lila.common.Iso.string[Id](Id.apply, _.value)
|
||||
|
||||
def toName(str: String) = str.trim take 100
|
||||
|
||||
sealed trait Visibility {
|
||||
|
@ -94,7 +97,7 @@ object Study {
|
|||
object From {
|
||||
case object Scratch extends From
|
||||
case class Game(id: String) extends From
|
||||
case class Study(id: String) extends From
|
||||
case class Study(id: Id) extends From
|
||||
}
|
||||
|
||||
case class Data(
|
||||
|
@ -120,11 +123,9 @@ object Study {
|
|||
|
||||
case class WithChaptersAndLiked(study: Study, chapters: Seq[String], liked: Boolean)
|
||||
|
||||
type ID = String
|
||||
|
||||
val idSize = 8
|
||||
|
||||
def makeId = scala.util.Random.alphanumeric take idSize mkString
|
||||
def makeId = Id(scala.util.Random.alphanumeric take idSize mkString)
|
||||
|
||||
def make(user: User, from: From) = {
|
||||
val owner = StudyMember(
|
||||
|
|
|
@ -30,14 +30,14 @@ final class StudyApi(
|
|||
|
||||
def byIds = studyRepo byOrderedIds _
|
||||
|
||||
def publicByIds(ids: Seq[String]) = byIds(ids) map { _.filter(_.isPublic) }
|
||||
def publicByIds(ids: Seq[Study.Id]) = byIds(ids) map { _.filter(_.isPublic) }
|
||||
|
||||
private def fetchAndFixChapter(id: Chapter.ID): Fu[Option[Chapter]] =
|
||||
chapterRepo.byId(id) flatMap {
|
||||
_ ?? { c => tagsFixer(c) map some }
|
||||
}
|
||||
|
||||
def byIdWithChapter(id: Study.ID): Fu[Option[Study.WithChapter]] = byId(id) flatMap {
|
||||
def byIdWithChapter(id: Study.Id): Fu[Option[Study.WithChapter]] = byId(id) flatMap {
|
||||
_ ?? { study =>
|
||||
fetchAndFixChapter(study.position.chapterId) flatMap {
|
||||
case None => chapterRepo.firstByStudy(study.id) flatMap {
|
||||
|
@ -52,7 +52,7 @@ final class StudyApi(
|
|||
}
|
||||
}
|
||||
|
||||
def byIdWithChapter(id: Study.ID, chapterId: Chapter.ID): Fu[Option[Study.WithChapter]] = byId(id) flatMap {
|
||||
def byIdWithChapter(id: Study.Id, chapterId: Chapter.ID): Fu[Option[Study.WithChapter]] = byId(id) flatMap {
|
||||
_ ?? { study =>
|
||||
fetchAndFixChapter(chapterId) map {
|
||||
_.filter(_.studyId == study.id) map { Study.WithChapter(study, _) }
|
||||
|
@ -77,7 +77,7 @@ final class StudyApi(
|
|||
studyRepo.insert(study) >>
|
||||
newChapters.map(chapterRepo.insert).sequenceFu >>- {
|
||||
chat ! lila.chat.actorApi.SystemTalk(
|
||||
study.id,
|
||||
study.id.value,
|
||||
s"Cloned from lichess.org/study/${prev.id}")
|
||||
} inject study.some
|
||||
}
|
||||
|
@ -92,23 +92,23 @@ final class StudyApi(
|
|||
case _ => fuccess(study)
|
||||
}
|
||||
|
||||
private def scheduleTimeline(studyId: Study.ID) = scheduler.scheduleOnce(1 minute) {
|
||||
private def scheduleTimeline(studyId: Study.Id) = scheduler.scheduleOnce(1 minute) {
|
||||
byId(studyId) foreach {
|
||||
_.filter(_.isPublic) foreach { study =>
|
||||
timeline ! (Propagate(StudyCreate(study.ownerId, study.id, study.name)) toFollowersOf study.ownerId)
|
||||
timeline ! (Propagate(StudyCreate(study.ownerId, study.id.value, study.name)) toFollowersOf study.ownerId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def talk(userId: User.ID, studyId: Study.ID, text: String, socket: ActorRef) = byId(studyId) foreach {
|
||||
def talk(userId: User.ID, studyId: Study.Id, text: String, socket: ActorRef) = byId(studyId) foreach {
|
||||
_ foreach { study =>
|
||||
(study canChat userId) ?? {
|
||||
chat ! lila.chat.actorApi.UserTalk(studyId, userId, text)
|
||||
chat ! lila.chat.actorApi.UserTalk(studyId.value, userId, text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def setPath(userId: User.ID, studyId: Study.ID, position: Position.Ref, uid: Uid) = sequenceStudy(studyId) { study =>
|
||||
def setPath(userId: User.ID, studyId: Study.Id, position: Position.Ref, uid: Uid) = sequenceStudy(studyId) { study =>
|
||||
Contribute(userId, study) {
|
||||
chapterRepo.byId(position.chapterId).map {
|
||||
_ filter { c =>
|
||||
|
@ -125,7 +125,7 @@ final class StudyApi(
|
|||
}
|
||||
}
|
||||
|
||||
def addNode(userId: User.ID, studyId: Study.ID, position: Position.Ref, node: Node, uid: Uid) = sequenceStudyWithChapter(studyId) {
|
||||
def addNode(userId: User.ID, studyId: Study.Id, position: Position.Ref, node: Node, uid: Uid) = sequenceStudyWithChapter(studyId) {
|
||||
case Study.WithChapter(study, chapter) => Contribute(userId, study) {
|
||||
chapter.addNode(node, position.path) match {
|
||||
case None => fufail(s"Invalid addNode $studyId $position $node") >>- reloadUid(study, uid)
|
||||
|
@ -150,7 +150,7 @@ final class StudyApi(
|
|||
}
|
||||
}
|
||||
|
||||
def deleteNodeAt(userId: User.ID, studyId: Study.ID, position: Position.Ref, uid: Uid) = sequenceStudyWithChapter(studyId) {
|
||||
def deleteNodeAt(userId: User.ID, studyId: Study.Id, position: Position.Ref, uid: Uid) = sequenceStudyWithChapter(studyId) {
|
||||
case Study.WithChapter(study, chapter) => Contribute(userId, study) {
|
||||
chapter.updateRoot { root =>
|
||||
root.withChildren(_.deleteNodeAt(position.path))
|
||||
|
@ -163,7 +163,7 @@ final class StudyApi(
|
|||
}
|
||||
}
|
||||
|
||||
def promote(userId: User.ID, studyId: Study.ID, position: Position.Ref, toMainline: Boolean, uid: Uid) = sequenceStudyWithChapter(studyId) {
|
||||
def promote(userId: User.ID, studyId: Study.Id, position: Position.Ref, toMainline: Boolean, uid: Uid) = sequenceStudyWithChapter(studyId) {
|
||||
case Study.WithChapter(study, chapter) => Contribute(userId, study) {
|
||||
chapter.updateRoot { root =>
|
||||
root.withChildren { children =>
|
||||
|
@ -179,14 +179,14 @@ final class StudyApi(
|
|||
}
|
||||
}
|
||||
|
||||
def setRole(byUserId: User.ID, studyId: Study.ID, userId: User.ID, roleStr: String) = sequenceStudy(studyId) { study =>
|
||||
def setRole(byUserId: User.ID, studyId: Study.Id, userId: User.ID, roleStr: String) = sequenceStudy(studyId) { study =>
|
||||
(study isOwner byUserId) ?? {
|
||||
val role = StudyMember.Role.byId.getOrElse(roleStr, StudyMember.Role.Read)
|
||||
studyRepo.setRole(study, userId, role) >>- reloadMembers(study)
|
||||
}
|
||||
}
|
||||
|
||||
def invite(byUserId: User.ID, studyId: Study.ID, username: String, socket: ActorRef) = sequenceStudy(studyId) { study =>
|
||||
def invite(byUserId: User.ID, studyId: Study.Id, username: String, socket: ActorRef) = sequenceStudy(studyId) { study =>
|
||||
(study.isOwner(byUserId) && study.nbMembers < 30) ?? {
|
||||
UserRepo.named(username).flatMap {
|
||||
_.filterNot(study.members.contains) ?? { user =>
|
||||
|
@ -197,13 +197,13 @@ final class StudyApi(
|
|||
}
|
||||
}
|
||||
|
||||
def kick(studyId: Study.ID, userId: User.ID) = sequenceStudy(studyId) { study =>
|
||||
def kick(studyId: Study.Id, userId: User.ID) = sequenceStudy(studyId) { study =>
|
||||
study.isMember(userId) ?? {
|
||||
studyRepo.removeMember(study, userId)
|
||||
} >>- reloadMembers(study) >>- indexStudy(study)
|
||||
}
|
||||
|
||||
def setShapes(userId: User.ID, studyId: Study.ID, position: Position.Ref, shapes: Shapes, uid: Uid) = sequenceStudy(studyId) { study =>
|
||||
def setShapes(userId: User.ID, studyId: Study.Id, position: Position.Ref, shapes: Shapes, uid: Uid) = sequenceStudy(studyId) { study =>
|
||||
Contribute(userId, study) {
|
||||
chapterRepo.byIdAndStudy(position.chapterId, study.id) flatMap {
|
||||
_ ?? { chapter =>
|
||||
|
@ -219,7 +219,7 @@ final class StudyApi(
|
|||
}
|
||||
}
|
||||
|
||||
def setTag(userId: User.ID, studyId: Study.ID, setTag: actorApi.SetTag, uid: Uid) = sequenceStudy(studyId) { study =>
|
||||
def setTag(userId: User.ID, studyId: Study.Id, setTag: actorApi.SetTag, uid: Uid) = sequenceStudy(studyId) { study =>
|
||||
Contribute(userId, study) {
|
||||
chapterRepo.byIdAndStudy(setTag.chapterId, studyId) flatMap {
|
||||
_ ?? { oldChapter =>
|
||||
|
@ -231,7 +231,7 @@ final class StudyApi(
|
|||
}
|
||||
}
|
||||
|
||||
def setComment(userId: User.ID, studyId: Study.ID, position: Position.Ref, text: Comment.Text, uid: Uid) = sequenceStudyWithChapter(studyId) {
|
||||
def setComment(userId: User.ID, studyId: Study.Id, position: Position.Ref, text: Comment.Text, uid: Uid) = sequenceStudyWithChapter(studyId) {
|
||||
case Study.WithChapter(study, chapter) => Contribute(userId, study) {
|
||||
(study.members get userId) ?? { byMember =>
|
||||
lightUser(userId) ?? { author =>
|
||||
|
@ -254,7 +254,7 @@ final class StudyApi(
|
|||
}
|
||||
}
|
||||
|
||||
def deleteComment(userId: User.ID, studyId: Study.ID, position: Position.Ref, id: Comment.Id, uid: Uid) = sequenceStudyWithChapter(studyId) {
|
||||
def deleteComment(userId: User.ID, studyId: Study.Id, position: Position.Ref, id: Comment.Id, uid: Uid) = sequenceStudyWithChapter(studyId) {
|
||||
case Study.WithChapter(study, chapter) => Contribute(userId, study) {
|
||||
(study.members get userId) ?? { byMember =>
|
||||
chapter.deleteComment(id, position.path) match {
|
||||
|
@ -268,7 +268,7 @@ final class StudyApi(
|
|||
}
|
||||
}
|
||||
|
||||
def toggleGlyph(userId: User.ID, studyId: Study.ID, position: Position.Ref, glyph: Glyph, uid: Uid) = sequenceStudyWithChapter(studyId) {
|
||||
def toggleGlyph(userId: User.ID, studyId: Study.Id, position: Position.Ref, glyph: Glyph, uid: Uid) = sequenceStudyWithChapter(studyId) {
|
||||
case Study.WithChapter(study, chapter) => Contribute(userId, study) {
|
||||
(study.members get userId) ?? { byMember =>
|
||||
chapter.toggleGlyph(glyph, position.path) match {
|
||||
|
@ -284,7 +284,7 @@ final class StudyApi(
|
|||
}
|
||||
}
|
||||
|
||||
def addChapter(byUserId: User.ID, studyId: Study.ID, data: ChapterMaker.Data, socket: ActorRef, uid: Uid) = sequenceStudy(studyId) { study =>
|
||||
def addChapter(byUserId: User.ID, studyId: Study.Id, data: ChapterMaker.Data, socket: ActorRef, uid: Uid) = sequenceStudy(studyId) { study =>
|
||||
Contribute(byUserId, study) {
|
||||
chapterRepo.nextOrderByStudy(study.id) flatMap { order =>
|
||||
chapterMaker(study, data, order, byUserId) flatMap {
|
||||
|
@ -303,7 +303,7 @@ final class StudyApi(
|
|||
}
|
||||
}
|
||||
|
||||
def setChapter(byUserId: User.ID, studyId: Study.ID, chapterId: Chapter.ID, socket: ActorRef, uid: Uid) = sequenceStudy(studyId) { study =>
|
||||
def setChapter(byUserId: User.ID, studyId: Study.Id, chapterId: Chapter.ID, socket: ActorRef, uid: Uid) = sequenceStudy(studyId) { study =>
|
||||
study.canContribute(byUserId) ?? doSetChapter(study, chapterId, socket, uid)
|
||||
}
|
||||
|
||||
|
@ -317,7 +317,7 @@ final class StudyApi(
|
|||
}
|
||||
}
|
||||
|
||||
def editChapter(byUserId: User.ID, studyId: Study.ID, data: ChapterMaker.EditData, socket: ActorRef, uid: Uid) = sequenceStudy(studyId) { study =>
|
||||
def editChapter(byUserId: User.ID, studyId: Study.Id, data: ChapterMaker.EditData, socket: ActorRef, uid: Uid) = sequenceStudy(studyId) { study =>
|
||||
Contribute(byUserId, study) {
|
||||
chapterRepo.byIdAndStudy(data.id, studyId) flatMap {
|
||||
_ ?? { chapter =>
|
||||
|
@ -355,7 +355,7 @@ final class StudyApi(
|
|||
}
|
||||
}
|
||||
|
||||
def deleteChapter(byUserId: User.ID, studyId: Study.ID, chapterId: Chapter.ID, socket: ActorRef, uid: Uid) = sequenceStudy(studyId) { study =>
|
||||
def deleteChapter(byUserId: User.ID, studyId: Study.Id, chapterId: Chapter.ID, socket: ActorRef, uid: Uid) = sequenceStudy(studyId) { study =>
|
||||
Contribute(byUserId, study) {
|
||||
chapterRepo.byIdAndStudy(chapterId, studyId) flatMap {
|
||||
_ ?? { chapter =>
|
||||
|
@ -372,13 +372,13 @@ final class StudyApi(
|
|||
}
|
||||
}
|
||||
|
||||
def sortChapters(byUserId: User.ID, studyId: Study.ID, chapterIds: List[Chapter.ID], socket: ActorRef, uid: Uid) = sequenceStudy(studyId) { study =>
|
||||
def sortChapters(byUserId: User.ID, studyId: Study.Id, chapterIds: List[Chapter.ID], socket: ActorRef, uid: Uid) = sequenceStudy(studyId) { study =>
|
||||
Contribute(byUserId, study) {
|
||||
chapterRepo.sort(study, chapterIds) >>- reloadChapters(study)
|
||||
}
|
||||
}
|
||||
|
||||
def editStudy(studyId: Study.ID, data: Study.Data) = sequenceStudy(studyId) { study =>
|
||||
def editStudy(studyId: Study.Id, data: Study.Data) = sequenceStudy(studyId) { study =>
|
||||
data.settings ?? { settings =>
|
||||
val newStudy = study.copy(
|
||||
name = Study toName data.name,
|
||||
|
@ -398,12 +398,12 @@ final class StudyApi(
|
|||
(indexer ! actorApi.RemoveStudy(study.id))
|
||||
}
|
||||
|
||||
def like(studyId: Study.ID, userId: User.ID, v: Boolean, socket: ActorRef, uid: Uid): Funit =
|
||||
def like(studyId: Study.Id, userId: User.ID, v: Boolean, socket: ActorRef, uid: Uid): Funit =
|
||||
studyRepo.like(studyId, userId, v) map { likes =>
|
||||
sendTo(studyId, Socket.SetLiking(Study.Liking(likes, v), uid))
|
||||
if (v) studyRepo.nameById(studyId) foreach {
|
||||
_ ?? { name =>
|
||||
timeline ! (Propagate(StudyLike(userId, studyId, name)) toFollowersOf userId)
|
||||
timeline ! (Propagate(StudyLike(userId, studyId.value, name)) toFollowersOf userId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -427,14 +427,14 @@ final class StudyApi(
|
|||
sendTo(study, Socket.ReloadChapters(chapters))
|
||||
}
|
||||
|
||||
private def sequenceStudy(studyId: String)(f: Study => Funit): Funit =
|
||||
private def sequenceStudy(studyId: Study.Id)(f: Study => Funit): Funit =
|
||||
byId(studyId) flatMap {
|
||||
_ ?? { study =>
|
||||
sequence(studyId)(f(study))
|
||||
}
|
||||
}
|
||||
|
||||
private def sequenceStudyWithChapter(studyId: String)(f: Study.WithChapter => Funit): Funit =
|
||||
private def sequenceStudyWithChapter(studyId: Study.Id)(f: Study.WithChapter => Funit): Funit =
|
||||
sequenceStudy(studyId) { study =>
|
||||
chapterRepo.byId(study.position.chapterId) flatMap {
|
||||
_ ?? { chapter =>
|
||||
|
@ -443,10 +443,10 @@ final class StudyApi(
|
|||
}
|
||||
}
|
||||
|
||||
private def sequence(studyId: String)(f: => Funit): Funit = {
|
||||
private def sequence(studyId: Study.Id)(f: => Funit): Funit = {
|
||||
val promise = scala.concurrent.Promise[Unit]
|
||||
val work = Sequencer.work(f, promise.some)
|
||||
sequencers ! Tell(studyId, work)
|
||||
sequencers ! Tell(studyId.value, work)
|
||||
promise.future
|
||||
}
|
||||
|
||||
|
@ -456,6 +456,6 @@ final class StudyApi(
|
|||
|
||||
private def sendTo(study: Study, msg: Any): Unit = sendTo(study.id, msg)
|
||||
|
||||
private def sendTo(studyId: Study.ID, msg: Any): Unit =
|
||||
socketHub ! Tell(studyId, msg)
|
||||
private def sendTo(studyId: Study.Id, msg: Any): Unit =
|
||||
socketHub ! Tell(studyId.value, msg)
|
||||
}
|
||||
|
|
|
@ -19,7 +19,10 @@ private final class StudyNotifier(
|
|||
_ ?? {
|
||||
socket ? HasUserId(invited.id) mapTo manifest[Boolean] map { isPresent =>
|
||||
study.owner.ifFalse(isPresent) foreach { owner =>
|
||||
val notificationContent = InvitedToStudy(InvitedToStudy.InvitedBy(owner.id), InvitedToStudy.StudyName(study.name), InvitedToStudy.StudyId(study.id))
|
||||
val notificationContent = InvitedToStudy(
|
||||
InvitedToStudy.InvitedBy(owner.id),
|
||||
InvitedToStudy.StudyName(study.name),
|
||||
InvitedToStudy.StudyId(study.id.value))
|
||||
val notification = Notification.make(Notification.Notifies(invited.id), notificationContent)
|
||||
notifyApi.addNotification(notification)
|
||||
}
|
||||
|
|
|
@ -17,9 +17,9 @@ final class StudyRepo(private[study] val coll: Coll) {
|
|||
"views" -> false,
|
||||
"rank" -> false)
|
||||
|
||||
def byId(id: Study.ID) = coll.find($id(id), projection).uno[Study]
|
||||
def byId(id: Study.Id) = coll.find($id(id), projection).uno[Study]
|
||||
|
||||
def byOrderedIds(ids: Seq[String]) = coll.byOrderedIds[Study](ids)(_.id)
|
||||
def byOrderedIds(ids: Seq[Study.Id]) = coll.byOrderedIds[Study, Study.Id](ids)(_.id)
|
||||
|
||||
def cursor(
|
||||
selector: Bdoc,
|
||||
|
@ -27,9 +27,9 @@ final class StudyRepo(private[study] val coll: Coll) {
|
|||
implicit cp: CursorProducer[Study]) =
|
||||
coll.find(selector).cursor[Study](readPreference)
|
||||
|
||||
def nameById(id: Study.ID) = coll.primitiveOne[String]($id(id), "name")
|
||||
def nameById(id: Study.Id) = coll.primitiveOne[String]($id(id), "name")
|
||||
|
||||
def exists(id: Study.ID) = coll.exists($id(id))
|
||||
def exists(id: Study.Id) = coll.exists($id(id))
|
||||
|
||||
private[study] def selectOwnerId(ownerId: User.ID) = $doc("ownerId" -> ownerId)
|
||||
private[study] def selectMemberId(memberId: User.ID) = $doc("uids" -> memberId)
|
||||
|
@ -57,10 +57,10 @@ final class StudyRepo(private[study] val coll: Coll) {
|
|||
|
||||
def delete(s: Study): Funit = coll.remove($id(s.id)).void
|
||||
|
||||
def membersById(id: Study.ID): Fu[Option[StudyMembers]] =
|
||||
def membersById(id: Study.Id): Fu[Option[StudyMembers]] =
|
||||
coll.primitiveOne[StudyMembers]($id(id), "members")
|
||||
|
||||
def setPosition(studyId: Study.ID, position: Position.Ref): Funit =
|
||||
def setPosition(studyId: Study.Id, position: Position.Ref): Funit =
|
||||
coll.update(
|
||||
$id(studyId),
|
||||
$set(
|
||||
|
@ -91,10 +91,10 @@ final class StudyRepo(private[study] val coll: Coll) {
|
|||
$set(s"members.$userId.role" -> role)
|
||||
).void
|
||||
|
||||
def uids(studyId: Study.ID): Fu[Set[User.ID]] =
|
||||
def uids(studyId: Study.Id): Fu[Set[User.ID]] =
|
||||
coll.primitiveOne[Set[User.ID]]($id(studyId), "uids") map (~_)
|
||||
|
||||
def like(studyId: Study.ID, userId: User.ID, v: Boolean): Fu[Study.Likes] =
|
||||
def like(studyId: Study.Id, userId: User.ID, v: Boolean): Fu[Study.Likes] =
|
||||
doLike(studyId, userId, v) >> countLikes(studyId).flatMap {
|
||||
case None => fuccess(Study.Likes(0))
|
||||
case Some((likes, createdAt)) => coll.update($id(studyId), $set(
|
||||
|
@ -106,14 +106,15 @@ final class StudyRepo(private[study] val coll: Coll) {
|
|||
def liked(study: Study, user: User): Fu[Boolean] =
|
||||
coll.exists($id(study.id) ++ selectLiker(user.id))
|
||||
|
||||
def filterLiked(user: User, studyIds: Seq[Study.ID]): Fu[Set[Study.ID]] =
|
||||
coll.primitive[Study.ID]($inIds(studyIds) ++ selectLiker(user.id), "_id").map(_.toSet)
|
||||
def filterLiked(user: User, studyIds: Seq[Study.Id]): Fu[Set[Study.Id]] =
|
||||
coll.primitive[Study.Id]($inIds(studyIds) ++ selectLiker(user.id), "_id").map(_.toSet)
|
||||
|
||||
def resetAllRanks: Fu[Int] = coll.find(
|
||||
|
||||
$empty, $doc("likes" -> true, "createdAt" -> true)
|
||||
).cursor[Bdoc]().foldWhileM(0) { (count, doc) =>
|
||||
~(for {
|
||||
id <- doc.getAs[Study.ID]("_id")
|
||||
id <- doc.getAs[Study.Id]("_id")
|
||||
likes <- doc.getAs[Study.Likes]("likes")
|
||||
createdAt <- doc.getAs[DateTime]("createdAt")
|
||||
} yield coll.update(
|
||||
|
@ -121,14 +122,14 @@ final class StudyRepo(private[study] val coll: Coll) {
|
|||
).void) inject Cursor.Cont(count + 1)
|
||||
}
|
||||
|
||||
private def doLike(studyId: Study.ID, userId: User.ID, v: Boolean): Funit =
|
||||
private def doLike(studyId: Study.Id, userId: User.ID, v: Boolean): Funit =
|
||||
coll.update(
|
||||
$id(studyId),
|
||||
if (v) $addToSet("likers" -> userId)
|
||||
else $pull("likers" -> userId)
|
||||
).void
|
||||
|
||||
private def countLikes(studyId: Study.ID): Fu[Option[(Study.Likes, DateTime)]] =
|
||||
private def countLikes(studyId: Study.Id): Fu[Option[(Study.Likes, DateTime)]] =
|
||||
coll.aggregate(
|
||||
Match($id(studyId)),
|
||||
List(Project($doc(
|
||||
|
|
|
@ -2,7 +2,7 @@ package lila.study
|
|||
package actorApi
|
||||
|
||||
case class SaveStudy(study: Study)
|
||||
case class RemoveStudy(id: Study.ID)
|
||||
case class RemoveStudy(id: Study.Id)
|
||||
case class SetTag(chapterId: Chapter.ID, name: String, value: String) {
|
||||
def tag = chess.format.pgn.Tag(name, value take 140)
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ final class Env(
|
|||
import lila.study.actorApi._
|
||||
def receive = {
|
||||
case SaveStudy(study) => api store study
|
||||
case RemoveStudy(id) => client deleteById Id(id)
|
||||
case RemoveStudy(id) => client deleteById Id(id.value)
|
||||
}
|
||||
}), name = ActorName)
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ final class StudySearchApi(
|
|||
|
||||
def search(query: Query, from: From, size: Size) = {
|
||||
client.search(query, from, size) flatMap { res =>
|
||||
studyRepo byOrderedIds res.ids
|
||||
studyRepo byOrderedIds res.ids.map(Study.Id.apply)
|
||||
}
|
||||
}.mon(_.study.search.query.time) >>- lila.mon.study.search.query.count()
|
||||
|
||||
|
@ -27,14 +27,14 @@ final class StudySearchApi(
|
|||
|
||||
def store(study: Study) = fuccess {
|
||||
indexThrottler ! LateMultiThrottler.work(
|
||||
id = study.id,
|
||||
id = study.id.value,
|
||||
run = studyRepo byId study.id flatMap { _ ?? doStore },
|
||||
delay = 30.seconds.some)
|
||||
}
|
||||
|
||||
private def doStore(study: Study) = {
|
||||
getChapters(study) flatMap { s =>
|
||||
client.store(Id(s.study.id), toDoc(s))
|
||||
client.store(Id(s.study.id.value), toDoc(s))
|
||||
}
|
||||
}.mon(_.study.search.index.time) >>- lila.mon.study.search.index.count()
|
||||
|
||||
|
|
|
@ -27,6 +27,8 @@ case class Team(
|
|||
|
||||
object Team {
|
||||
|
||||
type ID = String
|
||||
|
||||
def make(
|
||||
name: String,
|
||||
location: Option[String],
|
||||
|
|
|
@ -15,7 +15,7 @@ object TeamRepo {
|
|||
|
||||
type ID = String
|
||||
|
||||
def byOrderedIds(ids: Seq[String]) = coll.byOrderedIds[Team](ids)(_.id)
|
||||
def byOrderedIds(ids: Seq[Team.ID]) = coll.byOrderedIds[Team, Team.ID](ids)(_.id)
|
||||
|
||||
def cursor(
|
||||
selector: Bdoc,
|
||||
|
|
|
@ -45,7 +45,7 @@ object UserRepo {
|
|||
}
|
||||
|
||||
def byOrderedIds(ids: Seq[ID]): Fu[List[User]] =
|
||||
coll.byOrderedIds[User](ids)(_.id)
|
||||
coll.byOrderedIds[User, User.ID](ids)(_.id)
|
||||
|
||||
def enabledByIds(ids: Iterable[ID]): Fu[List[User]] =
|
||||
coll.list[User](enabledSelect ++ $inIds(ids))
|
||||
|
@ -76,25 +76,25 @@ object UserRepo {
|
|||
}
|
||||
|
||||
def usersFromSecondary(userIds: Seq[ID]): Fu[List[User]] =
|
||||
coll.byOrderedIds[User](userIds, readPreference = ReadPreference.secondaryPreferred)(_.id)
|
||||
coll.byOrderedIds[User, User.ID](userIds, readPreference = ReadPreference.secondaryPreferred)(_.id)
|
||||
|
||||
private[user] def allSortToints(nb: Int) =
|
||||
coll.find($empty).sort($sort desc F.toints).cursor[User]().gather[List](nb)
|
||||
|
||||
def usernameById(id: ID) =
|
||||
coll.primitiveOne[String]($id(id), F.username)
|
||||
coll.primitiveOne[User.ID]($id(id), F.username)
|
||||
|
||||
def usernamesByIds(ids: List[ID]) =
|
||||
coll.distinct[String, List](F.username, $inIds(ids).some)
|
||||
coll.distinct[User.ID, List](F.username, $inIds(ids).some)
|
||||
|
||||
def orderByGameCount(u1: String, u2: String): Fu[Option[(String, String)]] = {
|
||||
def orderByGameCount(u1: User.ID, u2: User.ID): Fu[Option[(User.ID, User.ID)]] = {
|
||||
coll.find(
|
||||
$inIds(List(u1, u2)),
|
||||
$doc(s"${F.count}.game" -> true)
|
||||
).cursor[Bdoc]().gather[List]() map { docs =>
|
||||
docs.sortBy {
|
||||
_.getAs[Bdoc](F.count).flatMap(_.getAs[BSONNumberLike]("game")).??(_.toInt)
|
||||
}.map(_.getAs[String]("_id")).flatten match {
|
||||
}.map(_.getAs[User.ID]("_id")).flatten match {
|
||||
case List(u1, u2) => (u1, u2).some
|
||||
case _ => none
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue