more work on weighted reports

reportWeight
Thibault Duplessis 2017-12-04 12:40:44 -05:00
parent e901327f97
commit 42aebed3a1
11 changed files with 103 additions and 15 deletions

View File

@ -3,8 +3,8 @@ package templating
import play.twirl.api.Html
import lila.user.{ User, UserContext }
import lila.security.{ Permission, Granter }
import lila.user.{ User, UserContext }
trait SecurityHelper {
@ -21,6 +21,9 @@ trait SecurityHelper {
Granter(permission)(user)
def reportScore(score: lila.report.Report.Score) = Html {
s"Score: <strong>${score.value.toInt}</strong>"
s"""<div class="score ${score.color}" title="Report score">${score.value.toInt}</div>"""
}
// def reportScore(score: lila.report.Report.Score) = Html {
// s"""<div class="score"><i>Score</i><strong>${score.value.toInt}</strong></div>"""
// }
}

View File

@ -30,7 +30,7 @@ moreCss = cssTag("report.css")) {
<table class="slist see">
<thead>
<tr>
<th></th>
<th>Score</th>
<th>Author</th>
<th>Reported</th>
<th>For</th>

View File

@ -38,7 +38,8 @@ db.report.aggregate(
text: r.text,
score: 30
})),
score: 30
score: 30,
open: true
};
db.report2.insert(report);

View File

@ -367,9 +367,7 @@ lazy val bookmark = module("bookmark", Seq(common, memo, db, hub, user, game)).s
)
lazy val report = module("report", Seq(common, db, user, game, security)).settings(
libraryDependencies ++= provided(
play.api, reactivemongo.driver
)
libraryDependencies ++= provided(play.api, reactivemongo.driver, reactivemongo.iteratees)
)
lazy val explorer = module("explorer", Seq(common, db, game, importer)).settings(

View File

@ -44,5 +44,6 @@ private[api] final class Cli(bus: lila.common.Bus) extends lila.common.Cli {
lila.studySearch.Env.current.cli.process orElse
lila.coach.Env.current.cli.process orElse
lila.evalCache.Env.current.cli.process orElse
lila.report.Env.current.cli.process orElse
process
}

View File

@ -40,6 +40,12 @@ final class Env(
lazy val modFilters = new ModReportFilter
def cli = new lila.common.Cli {
def process = {
case "report" :: "score" :: "reset" :: Nil => api.resetScores inject "done"
}
}
// api actor
system.actorOf(Props(new Actor {
def receive = {

View File

@ -25,6 +25,7 @@ case class Report(
def slug = _id
def closed = !open
def suspect = SuspectId(user)
def add(atom: Atom) = atomBy(atom.by).fold(copy(atoms = atom <:: atoms)) { existing =>
val newAtom = existing.copy(
@ -73,6 +74,11 @@ object Report {
case class Score(value: Double) extends AnyVal {
def +(s: Score) = Score(s.value + value)
def color =
if (value >= 150) "red"
else if (value >= 100) "orange"
else if (value >= 50) "yellow"
else "green"
}
implicit val scoreIso = lila.common.Iso.double[Score](Score.apply, _.value)
@ -90,8 +96,8 @@ object Report {
case class WithSuspect(report: Report, suspect: Suspect, isOnline: Boolean) {
def urgency: Int =
(nowSeconds - report.recentAtom.at.getSeconds).toInt +
(isOnline ?? (86400 * 5)) +
report.score.value.toInt +
(isOnline ?? 1000) +
(report.closed ?? Int.MinValue)
}

View File

@ -27,7 +27,7 @@ final class ReportApi(
private implicit val ReporterIdBSONHandler = stringIsoHandler[ReporterId](ReporterId.reporterIdIso)
private implicit val ScoreIdBSONHandler = doubleIsoHandler[Score](Report.scoreIso)
private implicit val AtomBSONHandler = Macros.handler[Atom]
private implicit val ReportBSONHandler = lila.db.BSON.LoggingHandler(logger)(Macros.handler[Report])
private implicit val ReportBSONHandler = Macros.handler[Report]
private lazy val scorer = new ReportScore(getAccuracy = accuracy.of)
@ -236,6 +236,8 @@ final class ReportApi(
}
} yield withNotes
private[report] def resetScores: Funit = scorer reset coll void
object accuracy {
private val cache = asyncCache.clearable[User.ID, Option[Int]](
@ -296,11 +298,13 @@ final class ReportApi(
ReadPreference.secondaryPreferred
)
private def findRecent(nb: Int, selector: Bdoc) =
private def findRecent(nb: Int, selector: Bdoc): Fu[List[Report]] = (nb > 0) ?? {
coll.find(selector).sort($sort.createdDesc).list[Report](nb)
}
private def findBest(nb: Int, selector: Bdoc) =
private def findBest(nb: Int, selector: Bdoc): Fu[List[Report]] = (nb > 0) ?? {
coll.find(selector).sort($sort desc "score").list[Report](nb)
}
private def selectRecent(suspect: Suspect, reason: Reason): Bdoc = $doc(
"atoms.0.at" $gt DateTime.now.minusDays(7),

View File

@ -1,6 +1,9 @@
package lila.report
import lila.user.User
import reactivemongo.bson._
import lila.db.dsl._
import lila.user.{ User, UserRepo }
private final class ReportScore(
getAccuracy: ReporterId => Fu[Option[Accuracy]]
@ -8,11 +11,48 @@ private final class ReportScore(
def apply(candidate: Report.Candidate): Fu[Report.Candidate.Scored] =
getAccuracy(candidate.reporter.id) map { accuracy =>
impl.accuracyScore(accuracy) + impl.reporterScore(candidate.reporter)
impl.accuracyScore(accuracy) +
impl.reporterScore(candidate.reporter) +
impl.textScore(candidate.reason, candidate.text)
} map { score =>
candidate scored Report.Score(score atLeast 0 atMost 100)
}
private[report] def reset(coll: Coll)(implicit handler: BSONDocumentHandler[Report]): Fu[Int] = {
import play.api.libs.iteratee._
import reactivemongo.play.iteratees.cursorProducer
coll.find($doc("open" -> true)).cursor[Report]().enumerator() |>>>
Iteratee.foldM[Report, Int](0) {
case (nb, report) => for {
newAtoms <- report.atoms.map { atom =>
candidateOf(report, atom) map {
_.fold(atom) { scored =>
atom.copy(score = scored.score)
}
}
}.toList.sequenceFu.map(_.toNel | report.atoms)
newReport = report.copy(atoms = newAtoms).recomputeScore
_ <- coll.update($id(report.id), newReport)
} yield {
if (nb % 100 == 0) logger.info(s"Score reset $nb")
nb + 1
}
}
}
private def candidateOf(report: Report, atom: Report.Atom): Fu[Option[Report.Candidate.Scored]] = for {
reporter <- UserRepo byId atom.by.value map2 Reporter.apply
suspect <- UserRepo named report.suspect.value map2 Suspect.apply
score <- (reporter |@| suspect).tupled ?? {
case (r, s) => apply(Report.Candidate(
reporter = r,
suspect = s,
reason = report.reason,
text = atom.text
)) map some
}
} yield score
private object impl {
def accuracyScore(a: Option[Accuracy]): Double = a ?? { accuracy =>
@ -27,5 +67,12 @@ private final class ReportScore(
def flagScore(user: User) =
(user.lameOrTroll) ?? -30d
private val gamePattern = """lichess.org/(\w{8,12})\b""".r.pattern
def textScore(reason: Reason, text: String) = {
(reason == Reason.Cheat || reason == Reason.Boost) &&
gamePattern.matcher(text).find
} ?? 20
}
}

View File

@ -5,9 +5,9 @@ import lila.user.User
case class Mod(user: User) extends AnyVal
case class Suspect(user: User) extends AnyVal {
def set(f: User => User) = copy(user = f(user))
}
case class SuspectId(value: User.ID) extends AnyVal
case class Victim(user: User) extends AnyVal

View File

@ -117,3 +117,25 @@
#report_list tr.new td:first-child {
border-left: 3px solid #3893E8;
}
.score {
white-space: nowrap;
}
.score {
font-weight: bold;
font-size: 1.2em;
padding: 0.3em 0.5em;
border-radius: 0.3em;
}
.score.green {
/* actually blue */
background-color: rgba(32, 119, 192, 0.4);
}
.score.yellow {
background-color: rgba(221, 207, 63, 0.4);
}
.score.orange {
background-color: rgba(231, 155, 100, 0.4);
}
.score.red {
background-color: rgba(231, 59, 56, 0.4);
}