lila/modules/activity/src/main/ActivityReadApi.scala

120 lines
3.7 KiB
Scala

package lila.activity
import org.joda.time.{ DateTime, Interval }
import lila.analyse.Analysis
import lila.db.dsl._
import lila.game.{ Game, Pov, GameRepo }
import lila.practice.PracticeStructure
import lila.user.User
import lila.user.UserRepo.lichessId
final class ActivityReadApi(
coll: Coll,
practiceApi: lila.practice.PracticeApi,
postApi: lila.forum.PostApi,
simulApi: lila.simul.SimulApi,
studyApi: lila.study.StudyApi,
tourLeaderApi: lila.tournament.LeaderboardApi
) {
import Activity._
import BSONHandlers._
import activities._
import model._
private val recentNb = 7
def recent(u: User): Fu[Vector[ActivityView]] = for {
as <- coll.find($doc("_id" -> $doc("$regex" -> s"^${u.id}:")))
.sort($sort desc "_id")
.gather[Activity, Vector](recentNb)
practiceStructure <- as.exists(_.practice.isDefined) ?? {
practiceApi.structure.get map some
}
views <- as.map { one(u, practiceStructure) _ }.sequenceFu
} yield addSignup(u.createdAt, views)
private def one(u: User, practiceStructure: Option[PracticeStructure])(a: Activity): Fu[ActivityView] = for {
posts <- a.posts ?? { p =>
postApi.liteViewsByIds(p.value.map(_.value)) dmap some
}
practice = (for {
p <- a.practice
struct <- practiceStructure
} yield p.value flatMap {
case (studyId, nb) => struct study studyId map (_ -> nb)
} toMap)
postView = posts.map { p =>
p.groupBy(_.topic).mapValues { posts =>
posts.map(_.post).sortBy(_.createdAt)
}
} filter (_.nonEmpty)
corresMoves <- a.corres ?? { corres =>
getPovs(a.id.userId, corres.movesIn) dmap {
_.map(corres.moves -> _)
}
}
corresEnds <- a.corres ?? { corres =>
getPovs(a.id.userId, corres.end) dmap {
_.map { povs =>
Score.make(povs) -> povs
}
}
}
simuls <- a.simuls.?? { simuls =>
simulApi byIds simuls.value.map(_.value) dmap some
}.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 =>
entries.nonEmpty option ActivityView.Tours(
nb = entries.size,
best = entries.sortBy(_.rankRatio.value).take(activities.maxSubEntries)
)
}
}
} yield ActivityView(
interval = a.interval,
games = a.games,
puzzles = a.puzzles,
practice = practice,
posts = postView,
simuls = simuls,
patron = a.patron,
corresMoves = corresMoves,
corresEnds = corresEnds,
follows = a.follows,
studies = studies,
teams = a.teams,
tours = tours
)
private def addSignup(at: DateTime, recent: Vector[ActivityView]) = {
val (found, views) = recent.foldLeft(false -> Vector.empty[ActivityView]) {
case ((false, as), a) if a.interval contains at => (true, as :+ a.copy(signup = true))
case ((found, as), a) => (found, as :+ a)
}
if (!found && views.size < recentNb && DateTime.now.minusDays(8).isBefore(at))
views :+ ActivityView(
interval = new Interval(at.withTimeAtStartOfDay, at.withTimeAtStartOfDay plusDays 1),
signup = true
)
else views
}
private def getPovs(userId: User.ID, gameIds: List[GameId]): Fu[Option[List[Pov]]] = gameIds.nonEmpty ?? {
GameRepo.gamesFromSecondary(gameIds.map(_.value)).dmap {
_.flatMap {
Pov.ofUserId(_, userId)
}.some.filter(_.nonEmpty)
}
}
private def makeIds(userId: User.ID, days: Int): List[Id] =
Day.recent(days).map { Id(userId, _) }
}