Rewrite trophy code: don't hardcode kinds
instead of hardcoding trophy kinds in the code, all of them go in a database collection 'trophyKind' to initialize, run the create-trophy-kinds.js script no changes to the 'trophy' collection will be needed whatsoeverglicko-over-time
parent
5f69d01369
commit
1a909ffacb
|
@ -9,7 +9,7 @@ import lila.forum.PostApi
|
|||
import lila.game.Crosstable
|
||||
import lila.relation.RelationApi
|
||||
import lila.security.Granter
|
||||
import lila.user.{ User, Trophy, Trophies, TrophyApi }
|
||||
import lila.user.{ User, SimplifiedTrophy, Trophy, Trophies, TrophyApi }
|
||||
|
||||
case class UserInfo(
|
||||
user: User,
|
||||
|
@ -36,28 +36,7 @@ case class UserInfo(
|
|||
|
||||
def completionRatePercent = completionRate.map { cr => math.round(cr * 100) }
|
||||
|
||||
lazy val allTrophies = List(
|
||||
Granter(_.PublicMod)(user) option Trophy(
|
||||
_id = "",
|
||||
user = user.id,
|
||||
kind = Trophy.Kind.Moderator,
|
||||
date = org.joda.time.DateTime.now
|
||||
),
|
||||
Granter(_.Developer)(user) option Trophy(
|
||||
_id = "",
|
||||
user = user.id,
|
||||
kind = Trophy.Kind.Developer,
|
||||
date = org.joda.time.DateTime.now
|
||||
),
|
||||
Granter(_.Verified)(user) option Trophy(
|
||||
_id = "",
|
||||
user = user.id,
|
||||
kind = Trophy.Kind.Verified,
|
||||
date = org.joda.time.DateTime.now
|
||||
)
|
||||
).flatten ::: trophies
|
||||
|
||||
def countTrophiesAndPerfCups = allTrophies.size + ranks.??(_.count(_._2 <= 100))
|
||||
def countTrophiesAndPerfCups = trophies.size + ranks.??(_.count(_._2 <= 100))
|
||||
}
|
||||
|
||||
object UserInfo {
|
||||
|
@ -146,36 +125,42 @@ object UserInfo {
|
|||
postApi.nbByUser(user.id) zip
|
||||
studyRepo.countByOwner(user.id) zip
|
||||
trophyApi.findByUser(user) zip
|
||||
shieldApi.active(user) zip
|
||||
revolutionApi.active(user) zip
|
||||
fetchTeamIds(user.id) zip
|
||||
fetchIsCoach(user) zip
|
||||
fetchIsStreamer(user) zip
|
||||
(user.count.rated >= 10).??(insightShare.grant(user, ctx.me)) zip
|
||||
getPlayTime(user) zip
|
||||
completionRate(user.id) flatMap {
|
||||
case ranks ~ ratingChart ~ nbFollowers ~ nbBlockers ~ nbPosts ~ nbStudies ~ trophies ~ shields ~ revols ~ teamIds ~ isCoach ~ isStreamer ~ insightVisible ~ playTime ~ completionRate =>
|
||||
(nbs.playing > 0) ?? isHostingSimul(user.id) map { hasSimul =>
|
||||
new UserInfo(
|
||||
user = user,
|
||||
ranks = ranks,
|
||||
nbs = nbs,
|
||||
hasSimul = hasSimul,
|
||||
ratingChart = ratingChart,
|
||||
nbFollowers = nbFollowers,
|
||||
nbBlockers = nbBlockers,
|
||||
nbPosts = nbPosts,
|
||||
nbStudies = nbStudies,
|
||||
playTime = playTime,
|
||||
trophies = trophies,
|
||||
shields = shields,
|
||||
revolutions = revols,
|
||||
teamIds = teamIds,
|
||||
isStreamer = isStreamer,
|
||||
isCoach = isCoach,
|
||||
insightVisible = insightVisible,
|
||||
completionRate = completionRate
|
||||
)
|
||||
}
|
||||
}
|
||||
trophyApi.roleBasedTrophies(
|
||||
user,
|
||||
Granter(_.PublicMod)(user),
|
||||
Granter(_.Developer)(user),
|
||||
Granter(_.Verified)(user)
|
||||
) zip
|
||||
shieldApi.active(user) zip
|
||||
revolutionApi.active(user) zip
|
||||
fetchTeamIds(user.id) zip
|
||||
fetchIsCoach(user) zip
|
||||
fetchIsStreamer(user) zip
|
||||
(user.count.rated >= 10).??(insightShare.grant(user, ctx.me)) zip
|
||||
getPlayTime(user) zip
|
||||
completionRate(user.id) flatMap {
|
||||
case ranks ~ ratingChart ~ nbFollowers ~ nbBlockers ~ nbPosts ~ nbStudies ~ trophies ~ roleTrophies ~ shields ~ revols ~ teamIds ~ isCoach ~ isStreamer ~ insightVisible ~ playTime ~ completionRate =>
|
||||
(nbs.playing > 0) ?? isHostingSimul(user.id) map { hasSimul =>
|
||||
new UserInfo(
|
||||
user = user,
|
||||
ranks = ranks,
|
||||
nbs = nbs,
|
||||
hasSimul = hasSimul,
|
||||
ratingChart = ratingChart,
|
||||
nbFollowers = nbFollowers,
|
||||
nbBlockers = nbBlockers,
|
||||
nbPosts = nbPosts,
|
||||
nbStudies = nbStudies,
|
||||
playTime = playTime,
|
||||
trophies = trophies ::: roleTrophies,
|
||||
shields = shields,
|
||||
revolutions = revols,
|
||||
teamIds = teamIds,
|
||||
isStreamer = isStreamer,
|
||||
isCoach = isCoach,
|
||||
insightVisible = insightVisible,
|
||||
completionRate = completionRate
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,8 +3,7 @@ package views.html.user.show
|
|||
import lila.api.Context
|
||||
import lila.app.templating.Environment._
|
||||
import lila.app.ui.ScalatagsTemplate._
|
||||
import lila.user.Trophy
|
||||
import Trophy.Kind
|
||||
import lila.user.{ Trophy, TrophyKind }
|
||||
import lila.user.User
|
||||
|
||||
import controllers.routes
|
||||
|
@ -12,7 +11,7 @@ import controllers.routes
|
|||
object otherTrophies {
|
||||
|
||||
def apply(u: User, info: lila.app.mashup.UserInfo)(implicit ctx: Context) = frag(
|
||||
info.allTrophies.filter(_.kind.klass.has("fire-trophy")).some.filter(_.nonEmpty) map { trophies =>
|
||||
info.trophies.filter(_.kind.klass.has("fire-trophy")).some.filter(_.nonEmpty) map { trophies =>
|
||||
div(cls := "stacked")(
|
||||
trophies.sorted.map { trophy =>
|
||||
trophy.kind.icon.map { iconChar =>
|
||||
|
@ -39,7 +38,7 @@ object otherTrophies {
|
|||
href := routes.Tournament.show(revol.tourId)
|
||||
)(revol.iconChar.toString)
|
||||
},
|
||||
info.allTrophies.find(_.kind == Kind.ZugMiracle).map { t =>
|
||||
info.trophies.find(_.kind._id == TrophyKind.zugMiracle).map { t =>
|
||||
frag(
|
||||
styleTag("""
|
||||
.trophy.zugMiracle {
|
||||
|
@ -53,7 +52,7 @@ object otherTrophies {
|
|||
height: 60px;
|
||||
}
|
||||
@keyframes psyche {
|
||||
100% { filter: hue-rotate(360deg); }
|
||||
100% { filter: hue-rotate(360deg); }
|
||||
}
|
||||
.trophy.zugMiracle:hover {
|
||||
transform: translateY(-9px);
|
||||
|
@ -64,15 +63,13 @@ object otherTrophies {
|
|||
)
|
||||
)
|
||||
},
|
||||
info.allTrophies.filter(t => t.kind == Kind.ZHWC17 || t.kind == Kind.ZHWC18).::: {
|
||||
info.allTrophies.filter(t => t.kind == Kind.AtomicWC16 || t.kind == Kind.AtomicWC17 || t.kind == Kind.AtomicWC18)
|
||||
}.map { t =>
|
||||
info.trophies.filter(_.kind.withCustomImage).map { t =>
|
||||
a(awardCls(t), href := t.kind.url, ariaTitle(t.kind.name),
|
||||
style := "width: 65px; margin: 0 3px!important;")(
|
||||
img(src := staticUrl(s"images/trophy/${t.kind.key}.png"), width := 65, height := 80)
|
||||
img(src := staticUrl(s"images/trophy/${t.kind._id}.png"), width := 65, height := 80)
|
||||
)
|
||||
},
|
||||
info.allTrophies.filter(_.kind.klass.has("icon3d")).sorted.map { trophy =>
|
||||
info.trophies.filter(_.kind.klass.has("icon3d")).sorted.map { trophy =>
|
||||
trophy.kind.icon.map { iconChar =>
|
||||
a(
|
||||
awardCls(trophy),
|
||||
|
@ -98,5 +95,5 @@ object otherTrophies {
|
|||
)("")
|
||||
)
|
||||
|
||||
private def awardCls(t: Trophy) = cls := s"trophy award ${t.kind.key} ${~t.kind.klass}"
|
||||
private def awardCls(t: Trophy) = cls := s"trophy award ${t.kind._id} ${~t.kind.klass}"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,128 @@
|
|||
db.trophyKind.drop();
|
||||
db.trophyKind.insert({
|
||||
_id: "zugMiracle",
|
||||
name: "Zug miracle",
|
||||
url: "//lichess.org/faq#trophies",
|
||||
order: NumberInt(1),
|
||||
withCustomImage: false
|
||||
});
|
||||
db.trophyKind.insert({
|
||||
_id: "wayOfBerserk",
|
||||
name: "The way of Berserk",
|
||||
icon: "`",
|
||||
url: "//lichess.org/faq#trophies",
|
||||
klass: "fire-trophy",
|
||||
order: NumberInt(2),
|
||||
withCustomImage: false
|
||||
});
|
||||
db.trophyKind.insert({
|
||||
_id: "marathonWinner",
|
||||
name: "Marathon Winner",
|
||||
icon: "\\",
|
||||
klass: "fire-trophy",
|
||||
order: NumberInt(3),
|
||||
withCustomImage: false
|
||||
});
|
||||
db.trophyKind.insert({
|
||||
_id: "marathonTopTen",
|
||||
name: "Marathon Top 10",
|
||||
icon: "\\",
|
||||
klass: "fire-trophy",
|
||||
order: NumberInt(4),
|
||||
withCustomImage: false
|
||||
});
|
||||
db.trophyKind.insert({
|
||||
_id: "marathonTopFifty",
|
||||
name: "Marathon Top 50",
|
||||
icon: "\\",
|
||||
klass: "fire-trophy",
|
||||
order: NumberInt(5),
|
||||
withCustomImage: false
|
||||
});
|
||||
db.trophyKind.insert({
|
||||
_id: "marathonTopHundred",
|
||||
name: "Marathon Top 100",
|
||||
icon: "\\",
|
||||
klass: "fire-trophy",
|
||||
order: NumberInt(6),
|
||||
withCustomImage: false
|
||||
});
|
||||
db.trophyKind.insert({
|
||||
_id: "marathonSurvivor",
|
||||
name: "Marathon #1 survivor",
|
||||
icon: ",",
|
||||
url: "//lichess.org/blog/VXF45yYAAPQgLH4d/chess-marathon-1",
|
||||
klass: "fire-trophy",
|
||||
order: NumberInt(7),
|
||||
withCustomImage: false
|
||||
});
|
||||
db.trophyKind.insert({
|
||||
_id: "bongcloudWarrior",
|
||||
name: "Bongcloud Warrior",
|
||||
icon: "~",
|
||||
url: "//lichess.org/forum/lichess-feedback/bongcloud-trophy",
|
||||
klass: "fire-trophy",
|
||||
order: NumberInt(8),
|
||||
withCustomImage: false
|
||||
});
|
||||
db.trophyKind.insert({
|
||||
_id: "developer",
|
||||
name: "Lichess developer",
|
||||
icon: "\ue000",
|
||||
url: "https://github.com/ornicar/lila/graphs/contributors",
|
||||
klass: "icon3d",
|
||||
order: NumberInt(100),
|
||||
withCustomImage: false
|
||||
});
|
||||
db.trophyKind.insert({
|
||||
_id: "moderator",
|
||||
name: "Lichess moderator",
|
||||
icon: "\ue002",
|
||||
url: "//lichess.org/report",
|
||||
klass: "icon3d",
|
||||
order: NumberInt(101),
|
||||
withCustomImage: false
|
||||
});
|
||||
db.trophyKind.insert({
|
||||
_id: "verified",
|
||||
name: "Verified account",
|
||||
icon: "E",
|
||||
klass: "icon3d",
|
||||
order: NumberInt(102),
|
||||
withCustomImage: false
|
||||
});
|
||||
db.trophyKind.insert({
|
||||
_id: "zhwc17",
|
||||
name: "Crazyhouse champion 2017",
|
||||
url: "//lichess.org/blog/WMnMzSEAAMgA3oAW/crazyhouse-world-championship-the-candidates",
|
||||
order: NumberInt(1),
|
||||
withCustomImage: true
|
||||
});
|
||||
db.trophyKind.insert({
|
||||
_id: "zhwc18",
|
||||
name: "Crazyhouse champion 2018",
|
||||
url: "//lichess.org/forum/team-crazyhouse-world-championship/opperwezen-the-2nd-cwc",
|
||||
order: NumberInt(1),
|
||||
withCustomImage: true
|
||||
});
|
||||
db.trophyKind.insert({
|
||||
_id: "atomicwc16",
|
||||
name: "Atomic World Champion 2016",
|
||||
url: "//lichess.org/forum/team-atomic-wc/championship-final",
|
||||
order: NumberInt(1),
|
||||
withCustomImage: true
|
||||
});
|
||||
db.trophyKind.insert({
|
||||
_id: "atomicwc17",
|
||||
name: "Atomic World Champion 2017",
|
||||
url: "//lichess.org/forum/team-atomic-wc/awc-2017-its-final-time",
|
||||
order: NumberInt(1),
|
||||
withCustomImage: true
|
||||
});
|
||||
db.trophyKind.insert({
|
||||
_id: "atomicwc18",
|
||||
name: "Atomic World Champion 2018",
|
||||
url: "//lichess.org/forum/team-atomic-wc/announcement-awc-2018",
|
||||
order: NumberInt(1),
|
||||
withCustomImage: true
|
||||
});
|
|
@ -556,6 +556,7 @@ user {
|
|||
user = user4
|
||||
note = note
|
||||
trophy = trophy
|
||||
trophyKind = trophyKind
|
||||
ranking = ranking
|
||||
}
|
||||
password.bpass {
|
||||
|
|
|
@ -176,17 +176,19 @@ final class TournamentApi(
|
|||
else if (tour.isCreated) wipe(tour)
|
||||
}
|
||||
|
||||
private def awardTrophies(tour: Tournament): Funit =
|
||||
private def awardTrophies(tour: Tournament): Funit = {
|
||||
import lila.user.TrophyKind._
|
||||
tour.schedule.??(_.freq == Schedule.Freq.Marathon) ?? {
|
||||
PlayerRepo.bestByTourWithRank(tour.id, 100).flatMap {
|
||||
_.map {
|
||||
case rp if rp.rank == 1 => trophyApi.award(rp.player.userId, _.MarathonWinner)
|
||||
case rp if rp.rank <= 10 => trophyApi.award(rp.player.userId, _.MarathonTopTen)
|
||||
case rp if rp.rank <= 50 => trophyApi.award(rp.player.userId, _.MarathonTopFifty)
|
||||
case rp => trophyApi.award(rp.player.userId, _.MarathonTopHundred)
|
||||
case rp if rp.rank == 1 => trophyApi.award(rp.player.userId, marathonWinner)
|
||||
case rp if rp.rank <= 10 => trophyApi.award(rp.player.userId, marathonTopTen)
|
||||
case rp if rp.rank <= 50 => trophyApi.award(rp.player.userId, marathonTopFifty)
|
||||
case rp => trophyApi.award(rp.player.userId, marathonTopHundred)
|
||||
}.sequenceFu.void
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def verdicts(tour: Tournament, me: Option[User], getUserTeamIds: User => Fu[TeamIdList]): Fu[Condition.All.WithVerdicts] = me match {
|
||||
case None => fuccess(tour.conditions.accepted)
|
||||
|
|
|
@ -23,6 +23,7 @@ final class Env(
|
|||
val CollectionUser = config getString "collection.user"
|
||||
val CollectionNote = config getString "collection.note"
|
||||
val CollectionTrophy = config getString "collection.trophy"
|
||||
val CollectionTrophyKind = config getString "collection.trophyKind"
|
||||
val CollectionRanking = config getString "collection.ranking"
|
||||
val PasswordBPassSecret = config getString "password.bpass.secret"
|
||||
}
|
||||
|
@ -41,7 +42,7 @@ final class Env(
|
|||
|
||||
lazy val noteApi = new NoteApi(db(CollectionNote), timeline, system.lilaBus)
|
||||
|
||||
lazy val trophyApi = new TrophyApi(db(CollectionTrophy))
|
||||
lazy val trophyApi = new TrophyApi(db(CollectionTrophy), db(CollectionTrophyKind))
|
||||
|
||||
lazy val rankingApi = new RankingApi(db(CollectionRanking), mongoCache, asyncCache, lightUser)
|
||||
|
||||
|
|
|
@ -2,10 +2,26 @@ package lila.user
|
|||
|
||||
import org.joda.time.DateTime
|
||||
|
||||
case class SimplifiedTrophy(
|
||||
_id: String,
|
||||
user: String,
|
||||
kind: String,
|
||||
date: DateTime
|
||||
)
|
||||
|
||||
object SimplifiedTrophy {
|
||||
def make(userId: String, kindKey: String): SimplifiedTrophy = SimplifiedTrophy(
|
||||
_id = ornicar.scalalib.Random nextString 8,
|
||||
user = userId,
|
||||
kind = kindKey,
|
||||
date = DateTime.now
|
||||
)
|
||||
}
|
||||
|
||||
case class Trophy(
|
||||
_id: String, // random
|
||||
user: String,
|
||||
kind: Trophy.Kind,
|
||||
kind: TrophyKind,
|
||||
date: DateTime
|
||||
) extends Ordered[Trophy] {
|
||||
|
||||
|
@ -16,179 +32,24 @@ case class Trophy(
|
|||
else Integer.compare(kind.order, other.kind.order)
|
||||
}
|
||||
|
||||
object Trophy {
|
||||
case class TrophyKind(
|
||||
_id: String,
|
||||
name: String,
|
||||
icon: Option[String],
|
||||
url: Option[String],
|
||||
klass: Option[String],
|
||||
order: Int,
|
||||
withCustomImage: Boolean
|
||||
)
|
||||
|
||||
sealed abstract class Kind(
|
||||
val key: String,
|
||||
val name: String,
|
||||
val icon: Option[String],
|
||||
val url: Option[String],
|
||||
val klass: Option[String],
|
||||
val order: Int
|
||||
)
|
||||
|
||||
object Kind {
|
||||
|
||||
object ZugMiracle extends Kind(
|
||||
key = "zugMiracle",
|
||||
name = "Zug miracle",
|
||||
icon = none,
|
||||
url = "//lichess.org/qa/259/how-do-you-get-a-zug-miracle-trophy".some,
|
||||
klass = none,
|
||||
order = 1
|
||||
)
|
||||
|
||||
object WayOfBerserk extends Kind(
|
||||
key = "wayOfBerserk",
|
||||
name = "The way of Berserk",
|
||||
icon = "`".some,
|
||||
url = "//lichess.org/qa/340/way-of-berserk-trophy".some,
|
||||
klass = "fire-trophy".some,
|
||||
order = 2
|
||||
)
|
||||
|
||||
object MarathonWinner extends Kind(
|
||||
key = "marathonWinner",
|
||||
name = "Marathon Winner",
|
||||
icon = "\\".some,
|
||||
url = none,
|
||||
klass = "fire-trophy".some,
|
||||
order = 3
|
||||
)
|
||||
|
||||
object MarathonTopTen extends Kind(
|
||||
key = "marathonTopTen",
|
||||
name = "Marathon Top 10",
|
||||
icon = "\\".some,
|
||||
url = none,
|
||||
klass = "fire-trophy".some,
|
||||
order = 4
|
||||
)
|
||||
|
||||
object MarathonTopFifty extends Kind(
|
||||
key = "marathonTopFifty",
|
||||
name = "Marathon Top 50",
|
||||
icon = "\\".some,
|
||||
url = none,
|
||||
klass = "fire-trophy".some,
|
||||
order = 5
|
||||
)
|
||||
|
||||
object MarathonTopHundred extends Kind(
|
||||
key = "marathonTopHundred",
|
||||
name = "Marathon Top 100",
|
||||
icon = "\\".some,
|
||||
url = none,
|
||||
klass = "fire-trophy".some,
|
||||
order = 6
|
||||
)
|
||||
|
||||
object MarathonSurvivor extends Kind(
|
||||
key = "marathonSurvivor",
|
||||
name = "Marathon #1 survivor",
|
||||
icon = ",".some,
|
||||
url = "//lichess.org/blog/VXF45yYAAPQgLH4d/chess-marathon-1".some,
|
||||
klass = "fire-trophy".some,
|
||||
order = 7
|
||||
)
|
||||
|
||||
object BongcloudWarrior extends Kind(
|
||||
key = "bongcloudWarrior",
|
||||
name = "Bongcloud Warrior",
|
||||
icon = "~".some,
|
||||
url = "//lichess.org/forum/lichess-feedback/bongcloud-trophy".some,
|
||||
klass = "fire-trophy".some,
|
||||
order = 8
|
||||
)
|
||||
|
||||
object Developer extends Kind(
|
||||
key = "developer",
|
||||
name = "Lichess developer",
|
||||
icon = "".some,
|
||||
url = "https://github.com/ornicar/lila/graphs/contributors".some,
|
||||
klass = "icon3d".some,
|
||||
order = 100
|
||||
)
|
||||
|
||||
object Moderator extends Kind(
|
||||
key = "moderator",
|
||||
name = "Lichess moderator",
|
||||
icon = "".some,
|
||||
url = "//lichess.org/report".some,
|
||||
"icon3d".some,
|
||||
order = 101
|
||||
)
|
||||
|
||||
object Verified extends Kind(
|
||||
key = "verified",
|
||||
name = "Verified account",
|
||||
icon = "E".some,
|
||||
url = none,
|
||||
"icon3d".some,
|
||||
order = 102
|
||||
)
|
||||
|
||||
object ZHWC17 extends Kind(
|
||||
key = "zhwc17",
|
||||
name = "Crazyhouse champion 2017",
|
||||
icon = none,
|
||||
url = "//lichess.org/blog/WMnMzSEAAMgA3oAW/crazyhouse-world-championship-the-candidates".some,
|
||||
klass = none,
|
||||
order = 1
|
||||
)
|
||||
|
||||
object ZHWC18 extends Kind(
|
||||
key = "zhwc18",
|
||||
name = "Crazyhouse champion 2018",
|
||||
icon = none,
|
||||
url = "//lichess.org/forum/team-crazyhouse-world-championship/opperwezen-the-2nd-cwc".some,
|
||||
klass = none,
|
||||
order = 1
|
||||
)
|
||||
|
||||
object AtomicWC16 extends Kind(
|
||||
key = "atomicwc16",
|
||||
name = "Atomic World Champion 2016",
|
||||
icon = none,
|
||||
url = "//lichess.org/forum/team-atomic-wc/championship-final".some,
|
||||
klass = none,
|
||||
order = 1
|
||||
)
|
||||
|
||||
object AtomicWC17 extends Kind(
|
||||
key = "atomicwc17",
|
||||
name = "Atomic World Champion 2017",
|
||||
icon = none,
|
||||
url = "//lichess.org/forum/team-atomic-wc/awc-2017-its-final-time".some,
|
||||
klass = none,
|
||||
order = 1
|
||||
)
|
||||
|
||||
object AtomicWC18 extends Kind(
|
||||
key = "atomicwc18",
|
||||
name = "Atomic World Champion 2018",
|
||||
icon = none,
|
||||
url = "//lichess.org/forum/team-atomic-wc/announcement-awc-2018".some,
|
||||
klass = none,
|
||||
order = 1
|
||||
)
|
||||
|
||||
val all = List(
|
||||
Developer, Moderator, Verified,
|
||||
MarathonTopHundred, MarathonTopTen, MarathonTopFifty, MarathonWinner,
|
||||
ZugMiracle, ZHWC17, ZHWC18,
|
||||
WayOfBerserk,
|
||||
MarathonSurvivor,
|
||||
BongcloudWarrior,
|
||||
AtomicWC16, AtomicWC17, AtomicWC18
|
||||
)
|
||||
val byKey: Map[String, Kind] = all.map { k => k.key -> k }(scala.collection.breakOut)
|
||||
}
|
||||
|
||||
def make(userId: String, kind: Trophy.Kind) = Trophy(
|
||||
_id = ornicar.scalalib.Random nextString 8,
|
||||
user = userId,
|
||||
kind = kind,
|
||||
date = DateTime.now
|
||||
)
|
||||
object TrophyKind {
|
||||
val marathonWinner = "marathonWinner"
|
||||
val marathonTopTen = "marathonTopTen"
|
||||
val marathonTopFifty = "marathonTopFifty"
|
||||
val marathonTopHundred = "marathonTopHundred"
|
||||
val moderator = "moderator"
|
||||
val developer = "developer"
|
||||
val verified = "verified"
|
||||
val zugMiracle = "zugMiracle"
|
||||
}
|
||||
|
||||
|
|
|
@ -4,23 +4,51 @@ import lila.db.dsl._
|
|||
import lila.db.BSON.BSONJodaDateTimeHandler
|
||||
import reactivemongo.bson._
|
||||
|
||||
final class TrophyApi(coll: Coll) {
|
||||
|
||||
private implicit val trophyKindBSONHandler = new BSONHandler[BSONString, Trophy.Kind] {
|
||||
def read(bsonString: BSONString): Trophy.Kind =
|
||||
Trophy.Kind.byKey get bsonString.value err s"No such trophy kind: ${bsonString.value}"
|
||||
def write(x: Trophy.Kind) = BSONString(x.key)
|
||||
}
|
||||
private implicit val trophyBSONHandler = Macros.handler[Trophy]
|
||||
|
||||
def award(userId: String, kind: Trophy.Kind): Funit =
|
||||
coll insert Trophy.make(userId, kind) void
|
||||
|
||||
def award(userId: String, kind: Trophy.Kind.type => Trophy.Kind): Funit =
|
||||
award(userId, kind(Trophy.Kind))
|
||||
|
||||
def awardMarathonWinner(userId: String): Funit = award(userId, Trophy.Kind.MarathonWinner)
|
||||
final class TrophyApi(coll: Coll, kindColl: Coll) {
|
||||
private implicit val trophyBSONHandler = Macros.handler[SimplifiedTrophy]
|
||||
private implicit val trophyKindBSONHandler = Macros.handler[TrophyKind]
|
||||
|
||||
def findByUser(user: User, max: Int = 50): Fu[List[Trophy]] =
|
||||
coll.find($doc("user" -> user.id)).list[Trophy](max)
|
||||
coll.find($doc("user" -> user.id)).list[SimplifiedTrophy](max) flatMap { l =>
|
||||
unsimplifyList(l)
|
||||
}
|
||||
|
||||
def roleBasedTrophies(user: User, isPublicMod: Boolean, isDev: Boolean, isVerified: Boolean): Fu[List[Trophy]] = List(
|
||||
isPublicMod option unsimplify(SimplifiedTrophy(
|
||||
_id = "",
|
||||
user = user.id,
|
||||
kind = TrophyKind.moderator,
|
||||
date = org.joda.time.DateTime.now
|
||||
)),
|
||||
isDev option unsimplify(SimplifiedTrophy(
|
||||
_id = "",
|
||||
user = user.id,
|
||||
kind = TrophyKind.developer,
|
||||
date = org.joda.time.DateTime.now
|
||||
)),
|
||||
isVerified option unsimplify(SimplifiedTrophy(
|
||||
_id = "",
|
||||
user = user.id,
|
||||
kind = TrophyKind.verified,
|
||||
date = org.joda.time.DateTime.now
|
||||
))
|
||||
).flatten.sequenceFu.map(_.flatten)
|
||||
|
||||
def unsimplifyList(simplified: List[SimplifiedTrophy]): Fu[List[Trophy]] =
|
||||
simplified.map({ t =>
|
||||
unsimplify(t)
|
||||
}).sequenceFu.map(_.flatten)
|
||||
|
||||
def unsimplify(simplified: SimplifiedTrophy): Fu[Option[Trophy]] =
|
||||
kindColl.byId[TrophyKind](simplified.kind) map2 { (kind: TrophyKind) =>
|
||||
Trophy(
|
||||
_id = simplified._id,
|
||||
user = simplified.user,
|
||||
kind = kind,
|
||||
date = simplified.date
|
||||
)
|
||||
}
|
||||
|
||||
def award(userId: String, kindKey: String): Funit =
|
||||
coll insert SimplifiedTrophy.make(userId, kindKey) void
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue