complete user activity feed API - closes #3473

pull/3486/merge
Thibault Duplessis 2017-08-19 10:31:28 -05:00
parent 7d75dc4044
commit 42e8ffe9c3
8 changed files with 81 additions and 26 deletions

View File

@ -316,8 +316,6 @@ name | type | default | description
### `GET /api/user/<username>/activity` fetch recent user activity
This API is a work in progress
```
> curl https://lichess.org/api/user/thibault/activityy
```

View File

@ -62,12 +62,12 @@ final class ActivityReadApi(
}
}
}
simuls <- a.simuls ?? { simuls =>
simuls <- a.simuls.?? { simuls =>
simulApi byIds simuls.value.map(_.value) dmap some
}
studies <- a.studies ?? { studies =>
}.map(_ filter (_.nonEmpty))
studies <- a.studies.?? { studies =>
studyApi publicIdNames studies.value map some
}
}.map(_ filter (_.nonEmpty))
tours <- a.games.exists(_.hasNonCorres) ?? {
val dateRange = a.date -> a.date.plusDays(1)
tourLeaderApi.timeRange(a.id.userId, dateRange) map { entries =>

View File

@ -16,7 +16,8 @@ final class Env(
studyApi: lila.study.StudyApi,
lightUserApi: lila.user.LightUserApi,
tourLeaderApi: lila.tournament.LeaderboardApi,
getTourName: lila.tournament.Tournament.ID => Option[String]
getTourName: lila.tournament.Tournament.ID => Option[String],
getTeamName: lila.team.Team.ID => Option[String]
) {
private val activityColl = db(config getString "collection.activity")
@ -37,7 +38,8 @@ final class Env(
lazy val jsonView = new JsonView(
lightUserApi = lightUserApi,
getTourName = getTourName
getTourName = getTourName,
getTeamName = getTeamName
)
system.lilaBus.subscribe(
@ -75,6 +77,7 @@ object Env {
studyApi = lila.study.Env.current.api,
lightUserApi = lila.user.Env.current.lightUserApi,
tourLeaderApi = lila.tournament.Env.current.leaderboardApi,
getTourName = lila.tournament.Env.current.cached.name _
getTourName = lila.tournament.Env.current.cached.name _,
getTeamName = lila.team.Env.current.cached.name _
)
}

View File

@ -5,8 +5,12 @@ import play.api.libs.json._
import lila.common.Iso
import lila.common.PimpedJson._
import lila.game.JsonView.colorWrites
import lila.game.Pov
import lila.rating.PerfType
import lila.simul.Simul
import lila.study.JsonView.studyIdNameWrites
import lila.team.Team
import lila.tournament.JsonView._
import lila.tournament.LeaderboardApi.{ Entry => TourEntry, Ratio => TourRatio }
import lila.tournament.Tournament
@ -18,7 +22,8 @@ import model._
final class JsonView(
lightUserApi: lila.user.LightUserApi,
getTourName: Tournament.ID => Option[String]
getTourName: Tournament.ID => Option[String],
getTeamName: Team.ID => Option[String]
) {
private object Writers {
@ -42,7 +47,8 @@ final class JsonView(
implicit val variantWrites: Writes[chess.variant.Variant] = Writes { v =>
JsString(v.key)
}
implicit val tourRatioWrites = intIsoWriter(Iso.int[TourRatio](r => TourRatio(r.toDouble), _.value.toInt))
// writes as percentage
implicit val tourRatioWrites = intIsoWriter(Iso.int[TourRatio](r => TourRatio(r.toDouble / 100), r => (r.value * 100).toInt))
implicit val tourEntryWrites = OWrites[TourEntry] { e =>
Json.obj(
"tournament" -> Json.obj(
@ -52,7 +58,7 @@ final class JsonView(
"nbGames" -> e.nbGames,
"score" -> e.score,
"rank" -> e.rank,
"rankRatio" -> e.rankRatio
"rankPercent" -> e.rankRatio
)
}
implicit val toursWrites = Json.writes[ActivityView.Tours]
@ -66,14 +72,40 @@ final class JsonView(
"score" -> Score(s.wins, s.losses, s.draws, none)
)
}
implicit val playerWrites = OWrites[lila.game.Player] { p =>
Json.obj()
.add("aiLevel" -> p.aiLevel)
.add("user" -> p.userId)
.add("rating" -> p.rating)
}
implicit val povWrites = OWrites[Pov] { p =>
Json.obj(
"id" -> p.gameId,
"color" -> p.color,
"url" -> s"/${p.gameId}/${p.color.name}",
"variant" -> p.game.variant,
"speed" -> p.game.speed.key,
"perf" -> lila.game.PerfPicker.key(p.game),
"rated" -> p.game.rated,
"opponent" -> p.opponent
)
}
implicit val followListWrites = Json.writes[FollowList]
implicit val followsWrites = Json.writes[Follows]
implicit val teamsWrites = Writes[Teams] { s =>
JsArray(s.value.map { id =>
Json.obj("url" -> s"/team/$id", "name" -> getTeamName(id))
})
}
implicit val patronWrites = Json.writes[Patron]
}
import Writers._
def apply(a: ActivityView, user: User): Fu[JsObject] = fuccess {
Json.obj("interval" -> a.interval)
.add("games", a.games map gamesWrites.writes)
.add("puzzles", a.puzzles map puzzlesWrites.writes)
.add("tournaments", a.tours map toursWrites.writes)
.add("games", a.games)
.add("puzzles", a.puzzles)
.add("tournaments", a.tours)
.add("practice", a.practice.map(_.toList.sortBy(-_._2) map {
case (study, nb) => Json.obj(
"url" -> s"/practice/-/${study.slug}/${study.id}",
@ -82,5 +114,27 @@ final class JsonView(
)
}))
.add("simuls", a.simuls.map(_ map simulWrites(user).writes))
.add("correspondenceMoves", a.corresMoves.map {
case (nb, povs) => Json.obj("nb" -> nb, "games" -> povs)
})
.add("correspondenceEnds", a.corresEnds.map {
case (score, povs) => Json.obj("score" -> score, "games" -> povs)
})
.add("follows" -> a.follows)
.add("studies" -> a.studies)
.add("teams" -> a.teams)
.add("posts" -> a.posts.map(_ map {
case (topic, posts) => Json.obj(
"topicUrl" -> s"/forum/${topic.categId}/${topic.slug}",
"topicName" -> topic.name,
"posts" -> posts.map { p =>
Json.obj(
"url" -> s"/forum/redirect/post/${p.id}",
"text" -> p.text.take(500)
)
}
)
}))
.add("patron" -> a.patron)
}
}

View File

@ -355,11 +355,4 @@ object Event {
"watchers" -> watchers
)
}
private implicit val colorWriter: Writes[Color] = Writes { c =>
JsString(c.name)
}
private implicit val statusWriter: OWrites[Status] = OWrites { s =>
Json.obj("id" -> s.id, "name" -> s.name)
}
}

View File

@ -17,7 +17,7 @@ object JsonView {
"rated" -> game.rated,
"initialFen" -> (initialFen | chess.format.Forsyth.initial),
"fen" -> (Forsyth >> game.toChess),
"player" -> game.turnColor.name,
"player" -> game.turnColor,
"turns" -> game.turns,
"startedAtTurn" -> game.startedAtTurn,
"source" -> game.source,
@ -26,12 +26,12 @@ object JsonView {
).add("threefold" -> game.toChessHistory.threefoldRepetition)
.add("boosted" -> game.boosted)
.add("tournamentId" -> game.tournamentId)
.add("winner" -> game.winnerColor.map(_.name))
.add("winner" -> game.winnerColor)
.add("lastMove" -> game.castleLastMoveTime.lastMoveString)
.add("check" -> game.check.map(_.key))
.add("rematch" -> game.next)
implicit val statusWriter: OWrites[chess.Status] = OWrites { s =>
implicit val statusWrites: OWrites[chess.Status] = OWrites { s =>
Json.obj(
"id" -> s.id,
"name" -> s.name
@ -115,4 +115,8 @@ object JsonView {
}
implicit val sourceWriter: Writes[Source] = Writes { s => JsString(s.name) }
implicit val colorWrites: Writes[Color] = Writes { c =>
JsString(c.name)
}
}

View File

@ -100,6 +100,9 @@ object JsonView {
implicit val studyIdWrites: Writes[Study.Id] = stringIsoWriter(Study.idIso)
implicit val studyNameWrites: Writes[Study.Name] = stringIsoWriter(Study.nameIso)
implicit val studyIdNameWrites = OWrites[Study.IdName] { s =>
Json.obj("id" -> s._id, "name" -> s.name)
}
implicit val chapterIdWrites: Writes[Chapter.Id] = stringIsoWriter(Chapter.idIso)
implicit val chapterNameWrites: Writes[Chapter.Name] = stringIsoWriter(Chapter.nameIso)

View File

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