Skip to content

Commit

Permalink
automatically set user title from notes containing FIDE URLs
Browse files Browse the repository at this point in the history
  • Loading branch information
ornicar committed Nov 13, 2018
1 parent d1bfc86 commit 0644acc
Show file tree
Hide file tree
Showing 10 changed files with 74 additions and 35 deletions.
4 changes: 2 additions & 2 deletions app/templating/UserHelper.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import lila.api.Context
import lila.common.LightUser
import lila.i18n.I18nKeys
import lila.rating.{ PerfType, Perf }
import lila.user.{ User, UserContext }
import lila.user.{ User, Title, UserContext }

trait UserHelper { self: I18nHelper with StringHelper with NumberHelper =>

Expand Down Expand Up @@ -140,7 +140,7 @@ trait UserHelper { self: I18nHelper with StringHelper with NumberHelper =>

def titleTag(title: Option[String]) = Html {
title.fold("") { t =>
s"""<span class="title" data-title="$t" title="${User titleName t}">$t</span>&nbsp;"""
s"""<span class="title" data-title="$t" title="${Title titleName t}">$t</span>&nbsp;"""
}
}

Expand Down
2 changes: 1 addition & 1 deletion app/views/user/mod/actions.scala.html
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@
</div>
@if(isGranted(_.SetTitle)) {
<form class="fide_title" method="post" action="@routes.Mod.setTitle(u.username)">
@base.form.select(lila.user.DataForm.title.fill(u.title)("title"), lila.user.User.titles, "No title".some)
@base.form.select(lila.user.DataForm.title.fill(u.title)("title"), lila.user.Title.all, "No title".some)
</form>
}
@if(isGranted(_.SetEmail)) {
Expand Down
2 changes: 1 addition & 1 deletion app/views/user/show/header.scala.html
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ <h1 class="lichess_title user_link @if(isOnline(u.id)){online}else{offline}">
</div>
}
} else {
@u.title.flatMap(lila.user.User.titlesMap.get).map { title =>
@u.title.flatMap(lila.user.Title.names.get).map { title =>
<p data-icon="E" class="honorific title text">@title</p>
}
}
Expand Down
4 changes: 2 additions & 2 deletions modules/game/src/main/Namer.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package lila.game

import lila.common.LightUser
import lila.user.User
import lila.user.{ User, Title }
import play.twirl.api.Html

object Namer {
Expand All @@ -13,7 +13,7 @@ object Namer {
p.aiLevel.fold(
p.userId.flatMap(lightUser).fold(lila.user.User.anonymous) { user =>
val title = (user.title ifTrue withTitle) ?? { t =>
s"""<span class="title" data-title="$t" title="${User titleName t}">$t</span>&nbsp;"""
s"""<span class="title" data-title="$t" title="${Title titleName t}">$t</span>&nbsp;"""
}
if (withRating) s"$title${user.name}&nbsp;(${ratingString(p)})"
else s"$title${user.name}"
Expand Down
4 changes: 2 additions & 2 deletions modules/mod/src/main/ModApi.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import lila.common.{ IpAddress, EmailAddress }
import lila.report.{ Mod, ModId, Suspect, SuspectId, Room }
import lila.security.Permission
import lila.security.{ Firewall, UserSpy, Store => SecurityStore }
import lila.user.{ User, UserRepo, LightUserApi }
import lila.user.{ User, UserRepo, Title, LightUserApi }

final class ModApi(
logApi: ModlogApi,
Expand Down Expand Up @@ -119,7 +119,7 @@ final class ModApi(
logApi.removeTitle(mod, user.id) >>-
lightUserApi.invalidate(user.id)
}
case Some(t) => User.titlesMap.get(t) ?? { tFull =>
case Some(t) => Title.names.get(t) ?? { tFull =>
UserRepo.addTitle(user.id, t) >>-
logApi.addTitle(mod, user.id, s"$t ($tFull)") >>-
lightUserApi.invalidate(user.id)
Expand Down
4 changes: 3 additions & 1 deletion modules/user/src/main/Env.scala
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ final class Env(

val jsonView = new JsonView(isOnline)

lazy val noteApi = new NoteApi(db(CollectionNote), timeline, system.lilaBus)
private lazy val titleUrl = new TitleUrl

lazy val noteApi = new NoteApi(db(CollectionNote), timeline, system.lilaBus, titleUrl)

lazy val trophyApi = new TrophyApi(db(CollectionTrophy))

Expand Down
7 changes: 6 additions & 1 deletion modules/user/src/main/NoteApi.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ case class UserNotes(user: User, notes: List[Note])
final class NoteApi(
coll: Coll,
timeline: akka.actor.ActorSelection,
bus: lila.common.Bus
bus: lila.common.Bus,
titleUrl: TitleUrl
) {

import reactivemongo.bson._
Expand Down Expand Up @@ -78,6 +79,10 @@ final class NoteApi(
mod = modOnly
), 'userNote)
}
} >> {
modOnly ?? titleUrl(text) flatMap {
_ ?? { UserRepo.addTitle(to.id, _) }
}
}

def byId(id: String): Fu[Option[Note]] =
Expand Down
51 changes: 51 additions & 0 deletions modules/user/src/main/TitleUrl.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package lila.user

import play.api.libs.ws.WS
import play.api.Play.current

case class Title(value: String) extends AnyVal with StringValue

object Title {

// important: names are as stated on FIDE profile pages
val all = Seq(
"GM" -> "Grandmaster",
"WGM" -> "Woman Grandmaster",
"IM" -> "International Master",
"WIM" -> "Woman Intl. Master",
"FM" -> "FIDE Master",
"WFM" -> "Woman FIDE Master",
"NM" -> "National Master",
"CM" -> "Candidate Master",
"WCM" -> "Woman Candidate Master",
"WNM" -> "Woman National Master",
"LM" -> "Lichess Master",
"BOT" -> "Chess Robot"
)

val bot = lila.common.LightUser.botTitle

val names = all.toMap
lazy val fromNames = all.map(_.swap).toMap

def titleName(title: String) = names get title getOrElse title
}

final class TitleUrl {

// https://ratings.fide.com/card.phtml?event=740411
private val FideProfileUrlRegex = """(?:https?:https://)ratings\.fide\.com/card\.phtml\?event=(\d+)""".r
// >&nbsp;FIDE title</td><td colspan=3 bgcolor=#efefef>&nbsp;Grandmaster</td>
private val FideProfileTitleRegex = """>&nbsp;FIDE title</td><td colspan=3 bgcolor=#efefef>&nbsp;([^<]+)</td>""".r.unanchored

def apply(url: String): Fu[Option[String]] = url.trim match {
case FideProfileUrlRegex(id) => parseIntOption(id) ?? fromFideProfile
case _ => fuccess(none)
}

private def fromFideProfile(id: Int): Fu[Option[String]] = {
WS.url(s"""https://ratings.fide.com/card.phtml?event=$id""").get().map(_.body) map {
case FideProfileTitleRegex(name) => Title.fromNames get name
}
}
}
25 changes: 3 additions & 22 deletions modules/user/src/main/User.scala
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ case class User(

def is(name: String) = id == User.normalize(name)

def isBot = title has User.botTitle
def isBot = title has Title.bot
def noBot = !isBot

def rankable = noBot && !rankban
Expand Down Expand Up @@ -168,6 +168,8 @@ object User {
case class TotpToken(value: String) extends AnyVal
case class PasswordAndToken(password: ClearPassword, token: Option[TotpToken])

case class Title(value: String) extends AnyVal with StringValue

case class PlayTime(total: Int, tv: Int) {
import org.joda.time.Period
def totalPeriod = new Period(total * 1000l)
Expand All @@ -191,27 +193,6 @@ object User {

def normalize(username: String) = username.toLowerCase

val titles = Seq(
"GM" -> "Grandmaster",
"WGM" -> "Woman Grandmaster",
"IM" -> "International Master",
"WIM" -> "Woman Intl. Master",
"FM" -> "FIDE Master",
"WFM" -> "Woman FIDE Master",
"NM" -> "National Master",
"CM" -> "Candidate Master",
"WCM" -> "Woman Candidate Master",
"WNM" -> "Woman National Master",
"LM" -> "Lichess Master",
"BOT" -> "Chess Robot"
)

val botTitle = LightUser.botTitle

val titlesMap = titles.toMap

def titleName(title: String) = titlesMap get title getOrElse title

object BSONFields {
val id = "_id"
val username = "username"
Expand Down
6 changes: 3 additions & 3 deletions modules/user/src/main/UserRepo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -343,11 +343,11 @@ object UserRepo {

def setBot(user: User): Funit =
if (user.count.game > 0) fufail("You already have games played. Make a new account.")
else coll.updateField($id(user.id), F.title, User.botTitle).void
else coll.updateField($id(user.id), F.title, Title.bot).void

private def botSelect(v: Boolean) =
if (v) $doc(F.title -> User.botTitle)
else $doc(F.title -> $ne(User.botTitle))
if (v) $doc(F.title -> Title.bot)
else $doc(F.title -> $ne(Title.bot))

private[user] def botIds = coll.distinctWithReadPreference[String, Set](
"_id",
Expand Down

0 comments on commit 0644acc

Please sign in to comment.