224 lines
6.7 KiB
Scala
224 lines
6.7 KiB
Scala
package lila.mod
|
|
|
|
import org.joda.time.DateTime
|
|
import org.joda.time.format.DateTimeFormat
|
|
import play.api.libs.json.Json
|
|
import reactivemongo.api.ReadPreference
|
|
import scala.concurrent.duration._
|
|
import scala.concurrent.duration.FiniteDuration
|
|
import scala.concurrent.ExecutionContext
|
|
import scala.util.Try
|
|
|
|
import lila.db.dsl._
|
|
import lila.report.Report
|
|
import lila.report.Room
|
|
import lila.user.User
|
|
|
|
final class ModActivity(repo: ModlogRepo, reportApi: lila.report.ReportApi, cacheApi: lila.memo.CacheApi)(
|
|
implicit ec: ExecutionContext
|
|
) {
|
|
|
|
import ModActivity._
|
|
|
|
def apply(who: String, period: String)(me: User): Fu[Result] =
|
|
cache.get((Who(who, me), Period(period)))
|
|
|
|
type CacheKey = (Who, Period)
|
|
|
|
private val cache = cacheApi[CacheKey, Result](64, "mod.activity") {
|
|
_.expireAfter[CacheKey, Result](
|
|
create = (key, _) =>
|
|
key match {
|
|
case (_, Period.Week) => 15.seconds
|
|
case (_, Period.Month) => 5.minutes
|
|
case (_, Period.Year) => 1.day
|
|
},
|
|
update = (_, _, current) => current,
|
|
read = (_, _, current) => current
|
|
).buildAsyncFuture((compute _).tupled)
|
|
}
|
|
|
|
private def compute(who: Who, period: Period): Fu[Result] =
|
|
repo.coll
|
|
.aggregateList(
|
|
maxDocs = 10_000,
|
|
readPreference = ReadPreference.secondaryPreferred
|
|
) { framework =>
|
|
import framework._
|
|
def dateToString(field: String): Bdoc =
|
|
$doc("$dateToString" -> $doc("format" -> "%Y-%m-%d", "date" -> s"$$$field"))
|
|
|
|
val reportPipeline = List(
|
|
Match(
|
|
$doc(
|
|
"open" -> false,
|
|
who match {
|
|
case Who.Me(userId) => "done.by" -> userId
|
|
case Who.Team => "done.by" $nin List(User.lichessId, "irwin")
|
|
},
|
|
"done.at" $gt Period.dateSince(period)
|
|
)
|
|
),
|
|
Group($arr(dateToString("done.at"), "$room"))("nb" -> SumAll)
|
|
)
|
|
|
|
Match(
|
|
$doc(
|
|
"human" -> true,
|
|
"date" $gt Period.dateSince(period)
|
|
) ++ (who match {
|
|
case Who.Me(userId) => $doc("mod" -> userId)
|
|
case Who.Team => $empty
|
|
})
|
|
) -> List(
|
|
Group($arr(dateToString("date"), "$action"))("nb" -> SumAll),
|
|
PipelineOperator(
|
|
$doc(
|
|
"$unionWith" -> $doc(
|
|
"coll" -> reportApi.coll.name,
|
|
"pipeline" -> reportPipeline
|
|
)
|
|
)
|
|
),
|
|
Sort(Descending("_id.0"))
|
|
)
|
|
}
|
|
.map { docs =>
|
|
for {
|
|
doc <- docs
|
|
id <- doc.getAsOpt[List[String]]("_id")
|
|
date <- id.headOption
|
|
key <- id lift 1
|
|
nb <- doc.int("nb")
|
|
} yield (date, key, nb)
|
|
}
|
|
.map {
|
|
_.foldLeft(Map.empty[String, Day]) { case (acc, (date, key, nb)) =>
|
|
acc.updated(
|
|
date, {
|
|
val row = acc.getOrElse(date, Day(Map.empty, Map.empty))
|
|
Room.byKey
|
|
.get(key)
|
|
.map(row.set(_, nb))
|
|
.orElse(Action.dbMap.get(key).map(row.set(_, nb)))
|
|
.getOrElse(row)
|
|
}
|
|
)
|
|
}
|
|
}
|
|
.map { data =>
|
|
Result(
|
|
who,
|
|
period,
|
|
data.toList.sortBy(_._1).reverse.flatMap { case (date, row) =>
|
|
Try(dateFormat parseDateTime date).toOption map { _ -> row }
|
|
}
|
|
)
|
|
}
|
|
}
|
|
|
|
object ModActivity {
|
|
|
|
case class Result(
|
|
who: Who,
|
|
period: Period,
|
|
data: List[(DateTime, Day)]
|
|
)
|
|
|
|
case class Day(actions: Map[Action, Int], reports: Map[Room, Int]) {
|
|
def set(action: Action, nb: Int) = copy(actions = actions.updated(action, nb))
|
|
def set(room: Room, nb: Int) = copy(reports = reports.updated(room, nb))
|
|
}
|
|
|
|
val dateFormat = DateTimeFormat forPattern "yyyy-MM-dd"
|
|
|
|
sealed trait Period {
|
|
def key = toString.toLowerCase
|
|
}
|
|
object Period {
|
|
case object Week extends Period
|
|
case object Month extends Period
|
|
case object Year extends Period
|
|
val all = List(Period.Week, Period.Month, Period.Year)
|
|
def apply(str: String): Period =
|
|
if (str == "year") Year
|
|
else if (str == "month") Month
|
|
else Week
|
|
def dateSince(period: Period) = period match {
|
|
case Period.Week => DateTime.now.minusWeeks(1)
|
|
case Period.Month => DateTime.now.minusMonths(1)
|
|
case Period.Year => DateTime.now.minusYears(1)
|
|
}
|
|
}
|
|
|
|
sealed abstract class Who(val key: String)
|
|
object Who {
|
|
case class Me(userId: User.ID) extends Who("me")
|
|
case object Team extends Who("team")
|
|
def apply(who: String, me: User) =
|
|
if (who == "me") Me(me.id) else Team
|
|
}
|
|
|
|
sealed trait Action
|
|
object Action {
|
|
case object Message extends Action
|
|
case object MarkCheat extends Action
|
|
case object MarkTroll extends Action
|
|
case object MarkBoost extends Action
|
|
case object CloseAccount extends Action
|
|
case object ChatTimeout extends Action
|
|
case object Appeal extends Action
|
|
case object SetEmail extends Action
|
|
case object Streamer extends Action
|
|
case object Blog extends Action
|
|
case object ForumAdmin extends Action
|
|
val dbMap = Map(
|
|
"modMessage" -> Message,
|
|
"engine" -> MarkCheat,
|
|
"unengine" -> MarkCheat,
|
|
"troll" -> MarkTroll,
|
|
"untroll" -> MarkTroll,
|
|
"booster" -> MarkBoost,
|
|
"unbooster" -> MarkBoost,
|
|
"alt" -> CloseAccount,
|
|
"unalt" -> CloseAccount,
|
|
"closeAccount" -> CloseAccount,
|
|
"reopenAccount" -> CloseAccount,
|
|
"chatTimeout" -> ChatTimeout,
|
|
"appealPost" -> Appeal,
|
|
"appealClose" -> Appeal,
|
|
"setEmail" -> SetEmail,
|
|
"streamerList" -> Streamer,
|
|
"streamerDecline" -> Streamer,
|
|
"streamerunlist" -> Streamer,
|
|
"streamerTier" -> Streamer,
|
|
"blogTier" -> Blog,
|
|
"deletePost" -> ForumAdmin,
|
|
"closeTopic" -> ForumAdmin
|
|
)
|
|
val all = dbMap.values.toList.distinct.sortBy(_.toString)
|
|
}
|
|
|
|
object json {
|
|
def apply(result: Result) = Json.obj(
|
|
"common" -> Json.obj(
|
|
"xaxis" -> result.data.map(_._1.getMillis)
|
|
),
|
|
"reports" -> Json.obj(
|
|
"series" -> Room.allButXfiles.map { room =>
|
|
serie(room.name, result.data.map(_._2.reports.getOrElse(room, 0)))
|
|
}
|
|
),
|
|
"actions" -> Json.obj(
|
|
"series" -> ModActivity.Action.all.map { act =>
|
|
serie(act.toString, result.data.map(_._2.actions.getOrElse(act, 0)))
|
|
}
|
|
)
|
|
)
|
|
private def serie(name: String, data: List[Int]) = Json.obj(
|
|
"name" -> name,
|
|
"data" -> data
|
|
)
|
|
}
|
|
}
|