Skip to content

Commit

Permalink
improve user API
Browse files Browse the repository at this point in the history
  • Loading branch information
ornicar committed Aug 8, 2014
1 parent 1197b06 commit d60fa49
Show file tree
Hide file tree
Showing 9 changed files with 132 additions and 89 deletions.
57 changes: 48 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,56 @@ drop me an email at [email protected], and we'll discuss it.
```javascript
{
"username": "thibault",
"title": null, // chess title like FM or LM (lichess master)
"url": "http://lichess.org/@/thibault", // profile url
"rating": 1503, // standard Glicko2 rating
"progress": 36, // rating change over the last ten games
"online": true, // is the player currently using lichess?
"playing": "http://lichess.org/abcdefgh", // game being played, if any
"engine": false // true if the user is known to use a chess engine
"engine": false, // true if the user is known to use a chess engine
"language": "en", // prefered language
"profile": {
"bio": "Developer of lichess",
"country": "FR",
"firstName": "Thibault",
"lastName": "Duplessis",
"location": "Paris"
},
"perfs": { // user performances in different games
"bullet": {
"games": 35, // number of rated games played
"rating": 1624, // Glicko2 rating
"rd": 80 // Glicko2 rating deviation
},
"chess960": {
"games": 1,
"rating": 1739,
"rd": 277
},
"classical": {
"games": 331,
"rating": 1603,
"rd": 65
},
"kingOfTheHill": {
"games": 3,
"rating": 1622,
"rd": 223
},
"puzzle": {
"games": 9,
"rating": 902,
"rd": 117
},
"standard": {
"games": 736,
"rating": 1576,
"rd": 79
},
"threeCheck": {
"games": 1,
"rating": 1662,
"rd": 290
}
}
}
```

Expand Down Expand Up @@ -91,12 +135,7 @@ name | type | default | description
{
"list": [
{
"username": "thibault",
"url": "http://lichess.org/@/thibault", // profile url
"rating": 1503, // standard Glicko2 rating
"progress": 36, // rating change over the last ten games
"online": true, // is the player currently using lichess?
"engine": false // true if the user is known to use a chess engine
... // see user document above
},
... // other users
]
Expand Down
2 changes: 1 addition & 1 deletion app/controllers/Account.scala
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ object Account extends LilaController {
me =>
negotiate(
html = notFound,
api = apiVersion => Ok(Env.user.jsonView me me).fuccess
api = apiVersion => Ok(Env.user.jsonView(me, extended = true)).fuccess
)
}

Expand Down
2 changes: 1 addition & 1 deletion app/controllers/Auth.scala
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ object Auth extends LilaController {
html = Redirect {
referrer.filter(_.nonEmpty) orElse req.session.get(api.AccessUri) getOrElse routes.Lobby.home.url
}.fuccess,
api = _ => Ok(Env.user.jsonView me u).fuccess
api = _ => Ok(Env.user.jsonView(u, extended = true)).fuccess
) map {
_ withCookies LilaCookie.withSession { session =>
session + ("sessionId" -> sessionId) - api.AccessUri
Expand Down
4 changes: 2 additions & 2 deletions app/mashup/UserInfo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ object UserInfo {
getRatingChart: User => Fu[Option[String]],
getRanks: String => Fu[Map[String, Int]],
getDonated: String => Fu[Int])(user: User, ctx: Context): Fu[UserInfo] =
countUsers() zip
getRanks(user.id) zip
countUsers() zip
getRanks(user.id) zip
((ctx is user) ?? { gameCached nbPlaying user.id map (_.some) }) zip
(ctx.me.filter(user!=) ?? { me => crosstableApi(me.id, user.id) }) zip
getRatingChart(user) zip
Expand Down
65 changes: 47 additions & 18 deletions doc/mobile-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -222,26 +222,55 @@ Set-Cookie: lila2="3b5cc8c80f0af258a31dc4fd1b5381cabe7388c7-sessionId=80q7V5stkK
```
```javascript
{
"id": "thibault",
"username": "thibault"
"title": null,
"rating": 1438, // shortcut to standard perf rating
"progress": -55, // rating progress over last 10 games
"count": { // number of games played
"ai": 256,
"draw": 72, // total draws
"drawH": 74, // draws against human only
"game": 2682, // total games played
"loss": 1471,
"lossH": 1403,
"rated": 1337,
"win": 1132,
"winH": 1131
"username": "thibault",
"title": null, // chess title like FM or LM (lichess master)
"online": true, // is the player currently using lichess?
"engine": false, // true if the user is known to use a chess engine
"language": "en", // prefered language
"profile": {
"bio": "Developer of lichess",
"country": "FR",
"firstName": "Thibault",
"lastName": "Duplessis",
"location": "Paris"
},
"perfs": { // user performances in different games
"bullet": {
"games": 35, // number of rated games played
"rating": 1624, // Glicko2 rating
"rd": 80 // Glicko2 rating deviation
},
"chess960": {
"games": 1,
"rating": 1739,
"rd": 277
},
"classical": {
"games": 331,
"rating": 1603,
"rd": 65
},
"kingOfTheHill": {
"games": 3,
"rating": 1622,
"rd": 223
},
"puzzle": {
"games": 9,
"rating": 1902,
"rd": 117
},
"standard": {
"games": 736,
"rating": 1576,
"rd": 79
},
"playTime": { // total seconds spent playing
"total": 558788,
"tv": 6255
"threeCheck": {
"games": 1,
"rating": 1662,
"rd": 290
}
}
}
```

Expand Down
4 changes: 2 additions & 2 deletions modules/api/src/main/Env.scala
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ final class Env(
}

val userApi = new UserApi(
jsonView = userEnv.jsonView,
makeUrl = apiUrl,
apiToken = apiToken,
userIdsSharingIp = userIdsSharingIp,
isOnline = userEnv.isOnline)
userIdsSharingIp = userIdsSharingIp)

val gameApi = new GameApi(
makeUrl = apiUrl,
Expand Down
36 changes: 5 additions & 31 deletions modules/api/src/main/UserApi.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,34 +9,14 @@ import lila.game.GameRepo
import lila.hub.actorApi.{ router => R }
import lila.rating.Perf
import lila.user.tube.userTube
import lila.user.{ UserRepo, User, Perfs }
import lila.user.{ UserRepo, User, Perfs, Profile }
import makeTimeout.short

private[api] final class UserApi(
jsonView: lila.user.JsonView,
makeUrl: Any => Fu[String],
apiToken: String,
userIdsSharingIp: String => Fu[List[String]],
isOnline: String => Boolean) {

private implicit val perfWrites: Writes[Perf] = Writes { o =>
Json.obj(
"nbGames" -> o.nb,
"rating" -> o.glicko.rating.toInt,
"deviation" -> o.glicko.deviation.toInt)
}
private implicit val perfsWrites: Writes[Perfs] = Writes { o =>
JsObject(o.perfs map {
case (name, perf) => name -> perfWrites.writes(perf)
})
}
private implicit val userWrites: OWrites[User] = OWrites { u =>
Json.obj(
"id" -> u.id,
"username" -> u.username,
"rating" -> u.rating,
"rd" -> u.perfs.standard.glicko.deviation,
"progress" -> u.progress)
}
userIdsSharingIp: String => Fu[List[String]]) {

def list(
team: Option[String],
Expand All @@ -52,11 +32,7 @@ private[api] final class UserApi(
Json.obj(
"list" -> JsArray(
users zip urls map {
case (u, url) => userWrites.writes(u) ++ Json.obj(
"url" -> url,
"online" -> isOnline(u.id),
"engine" -> u.engine
).noNull
case (u, url) => jsonView(u, extended = team.isDefined) ++ Json.obj("url" -> url).noNull
}
)
)
Expand All @@ -71,11 +47,9 @@ private[api] final class UserApi(
case ((gameOption, userUrl), knownEngines) => gameOption ?? { g =>
makeUrl(R.Watcher(g.id, g.firstPlayer.color.name)) map (_.some)
} map { gameUrlOption =>
userWrites.writes(u) ++ Json.obj(
jsonView(u, extended = true) ++ Json.obj(
"url" -> userUrl,
"online" -> isOnline(u.id),
"playing" -> gameUrlOption,
"engine" -> u.engine,
"knownEnginesSharingIp" -> knownEngines
).noNull
}
Expand Down
2 changes: 1 addition & 1 deletion modules/user/src/main/Env.scala
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ final class Env(

lazy val noteApi = new NoteApi(db(CollectionNote), timeline)

lazy val jsonView = new JsonView
lazy val jsonView = new JsonView(isOnline)

val forms = DataForm

Expand Down
49 changes: 25 additions & 24 deletions modules/user/src/main/JsonView.scala
Original file line number Diff line number Diff line change
@@ -1,32 +1,33 @@
package lila.user

import lila.rating.{ Perf, Glicko }
import play.api.libs.json.Json
import play.api.libs.json._
import lila.common.PimpedJson._

final class JsonView {
final class JsonView(isOnline: String => Boolean) {

private implicit val countWrites = Json.writes[Count]
private implicit val glickoWrite = Json.writes[Glicko]
private implicit val perfWrite = Json.writes[Perf]
private implicit val perfsWrites = Json.writes[Perfs]

def me(u: User) = user(u)

def user(u: User) = Json.obj(
"id" -> u.id,
"username" -> u.username,
"title" -> u.title,
"rating" -> u.rating,
"rd" -> u.perfs.standard.glicko.deviation,
"progress" -> u.progress,
"playTime" -> u.playTime.map { p =>
Json.obj(
"total" -> p.total,
"tv" -> p.tv)
private implicit val perfWrites: Writes[Perf] = Writes { o =>
Json.obj(
"games" -> o.nb,
"rating" -> o.glicko.rating.toInt,
"rd" -> o.glicko.deviation.toInt)
}
private implicit val perfsWrites: Writes[Perfs] = Writes { o =>
JsObject(o.perfsMap.toList map {
case (name, perf) => name -> perfWrites.writes(perf)
})
}
private implicit val profileWrites = Json.writes[Profile]

def full(u: User) = user(u) ++ Json.obj(
"count" -> countWrites.writes(u.count),
"perfs" -> perfsWrites.writes(u.perfs)
)
def apply(u: User, extended: Boolean) = Json.obj(
"id" -> u.id,
"username" -> u.username
) ++ extended.??(Json.obj(
"title" -> u.title,
"online" -> isOnline(u.id),
"engine" -> u.engine,
"language" -> u.lang,
"profile" -> u.profile.??(profileWrites.writes).noNull,
"perfs" -> u.perfs
)).noNull
}

0 comments on commit d60fa49

Please sign in to comment.