mod progress WIP

pull/9131/head
Thibault Duplessis 2021-06-07 13:41:00 +02:00
parent 4589650e7e
commit 1d9b289e1d
13 changed files with 101 additions and 170 deletions

View File

@ -6,6 +6,7 @@ import lila.app.ui.ScalatagsTemplate._
import lila.mod.ModProgress._
import controllers.routes
import lila.report.Room
object progress {
@ -23,8 +24,11 @@ object progress {
thead(
tr(
th("Date"),
Room.all.map { r =>
th("Report", br, r.name)
},
Action.all.map { a =>
th(a.toString)
th("Action", br, a.toString)
}
)
),
@ -32,6 +36,9 @@ object progress {
p.data.map { case (date, row) =>
tr(
th(showDate(date)),
Room.all.map { r =>
td(~row.reports.get(r))
},
Action.all.map { a =>
td(~row.actions.get(a))
}

View File

@ -62,7 +62,7 @@ object list {
td(
r.inquiry match {
case None =>
if (r.processedBy.isDefined)
if (r.done.isDefined)
postForm(action := routes.Report.inquiry(r.id), cls := "reopen")(
submitButton(dataIcon := "G", cls := "text button button-metal")("Reopen")
)

View File

@ -0,0 +1,18 @@
db.modlog.update({ human: { $ne: true }, mod: { $ne: 'lichess' } }, { $set: { human: true } }, { multi: true });
db.modlog.createIndex({ mod: 1, date: -1 }, { partialFilterExpression: { human: true } });
db.report2.find({ processedBy: { $exists: 1 } }).forEach(r =>
db.report2.update(
{ _id: r._id },
{
$unset: { processedBy: 1 },
$set: {
done: {
by: r.processedBy,
at: r.atoms[0].at,
},
},
}
)
);
db.report2.createIndex({ 'done.at': -1 }, { partialFilterExpression: { open: false } });

View File

@ -1,3 +0,0 @@
db.modlog.update({ mod: { $ne: 'lichess' } }, { $set: { human: true } }, { multi: true });
db.modlog.createIndex({ mod: 1, date: -1 }, { partialFilterExpression: { human: true } });

View File

@ -1,54 +0,0 @@
db.report2.drop();
db.report
.aggregate(
[
{
$group: {
_id: {
user: '$user',
reason: '$reason',
processedBy: '$processedBy',
},
reports: { $push: '$$ROOT' },
},
},
],
{ allowDiskUse: true }
)
.toArray()
.forEach(group => {
var reports = group.reports;
var first = reports[0];
var atoms = [];
reports.forEach(r => {
var same = atoms.find(a => a.by === r.createdBy);
if (same) same.text += '\n\n' + r.text;
else
atoms.push({
by: r.createdBy,
at: r.createdAt,
text: r.text,
score: 30,
});
});
var report = {
_id: first._id,
user: first.user,
reason: first.reason,
room: first.room,
atoms: atoms,
score: atoms.reduce((acc, atom) => acc + atom.score, 0),
open: !first.processedBy,
};
if (first.processedBy) report.processedBy = first.processedBy;
db.report2.insert(report);
});
db.report2.createIndex({ room: 1, score: -1 }, { partialFilterExpression: { open: true }, name: 'best_open' });
db.report2.createIndex({ 'inquiry.mod': 1 }, { partialFilterExpression: { 'inquiry.mod': { $exists: true } } });
db.report2.createIndex({ user: 1 });
db.report2.createIndex({ 'atoms.by': 1 });

View File

@ -1,23 +0,0 @@
db.report2.update({ room: 'coms' }, { $set: { room: 'comm' } }, { multi: 1 });
db.report2.distinct('user', { room: 'comm' }).forEach(user => {
const reports = db.report2.find({ user: user, room: 'comm' }).toArray();
const report = reports[0];
const others = reports.slice(1);
report.reason = 'comm';
others.forEach(rep => {
report.atoms = report.atoms.concat(rep.atoms);
report.score = report.score + rep.score;
report.open = report.open && rep.open;
report.processedBy = report.processedBy || rep.processedBy;
});
if (!report.processedBy || report.open) delete report.processedBy;
report.atoms.sort((a, b) => a.at > b.at);
db.report2.update({ _id: report._id }, { $set: report });
if (others.length) db.report2.remove({ _id: { $in: others.map(r => r._id) } });
});

View File

@ -1,12 +0,0 @@
var lastWeek = new Date(Date.now() - 1000 * 60 * 60 * 24 * 7);
db.report.find().forEach(r => {
var room = 'others';
if (!r.processedBy && r.createdAt < lastWeek) room = 'xfiles';
else if (r.reason === 'cheat') room = 'cheat';
else if (r.reason === 'cheatprint') room = 'print';
else if (r.reason === 'troll') room = 'coms';
else if (r.reason === 'insult') room = 'coms';
db.report.update({ _id: r._id }, { $set: { room: room } });
});

View File

@ -1,32 +0,0 @@
db.report
.find({
processedBy: {
$exists: false,
},
})
.sort({
createdAt: -1,
})
.skip(150)
.limit(1)
.toArray()
.forEach(function (report) {
db.report.update(
{
processedBy: {
$exists: false,
},
createdAt: {
$gt: report.createdAt,
},
},
{
$set: {
processedBy: 'lichess-sweep',
},
},
{
multi: true,
}
);
});

View File

@ -135,12 +135,12 @@ final class Gamify(
import framework._
Match(
$doc(
"atoms.0.at" -> dateRange(after, before),
"room" $in Room.all, // required to make use of the mongodb index room+atoms.0.at
"processedBy" -> $nin(hidden)
"done.at" -> dateRange(after, before),
"done.by" -> $nin(hidden),
"open" -> false
)
) -> List(
GroupField("processedBy")(
GroupField("done.by")(
"nb" -> Sum(
$doc(
"$cond" -> $arr($doc("$eq" -> $arr("room", Room.Cheat.key)), 3, 1)
@ -150,11 +150,12 @@ final class Gamify(
Sort(Descending("nb"))
)
}
.map {
_.flatMap { obj =>
import cats.implicits._
(obj.string("_id"), obj.int("nb")) mapN ModCount.apply
}
.map { docs =>
for {
doc <- docs
id <- doc.string("_id")
nb <- doc.int("nb")
} yield ModCount(id, nb)
}
}

View File

@ -8,8 +8,10 @@ import scala.util.Try
import lila.db.dsl._
import lila.user.User
import lila.report.Report
import lila.report.Room
final class ModProgress(repo: ModlogRepo)(implicit ec: ExecutionContext) {
final class ModProgress(repo: ModlogRepo, reportApi: lila.report.ReportApi)(implicit ec: ExecutionContext) {
import ModProgress._
@ -23,48 +25,72 @@ final class ModProgress(repo: ModlogRepo)(implicit ec: ExecutionContext) {
readPreference = ReadPreference.secondaryPreferred
) { framework =>
import framework._
val dateSince = period match {
case Period.Week => DateTime.now.minusWeeks(1)
case Period.Month => DateTime.now.minusMonths(1)
case Period.Year => DateTime.now.minusYears(1)
}
def dateToString(field: String): Bdoc =
$doc("$dateToString" -> $doc("format" -> "%Y-%m-%d", "date" -> s"$$$field"))
Match(
$doc(
"human" -> true,
"date" $gt (period match {
case Period.Week => DateTime.now.minusWeeks(1)
case Period.Month => DateTime.now.minusMonths(1)
case Period.Year => DateTime.now.minusYears(1)
})
"date" $gt dateSince
) ++ (who match {
case Who.Me(userId) => $doc("mod" -> userId)
case Who.Team => $empty
})
) -> List(
Group(
$arr(
$doc("$dateToString" -> $doc("format" -> "%Y-%m-%d", "date" -> "$date")),
"$action"
Group($arr(dateToString("date"), "$action"))("nb" -> SumAll),
PipelineOperator(
$doc(
"$unionWith" -> $doc(
"coll" -> reportApi.coll.name,
"pipeline" -> List(
Match(
$doc(
"open" -> false,
"done.by" $nin List(User.lichessId, "irwin"),
"done.at" $gt dateSince
)
),
Group($arr(dateToString("done.at"), "$room"))("nb" -> SumAll)
)
)
)
)("nb" -> SumAll)
),
Sort(Descending("_id.0"))
)
}
.map { docs =>
for {
doc <- docs
id <- doc.getAsOpt[List[String]]("_id")
date <- id.headOption
actionKey <- id lift 1
action <- Action.dbMap get actionKey
nb <- doc.int("nb")
} yield (date, action, nb)
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, Map[Action, Int]]) { case (acc, (date, action, nb)) =>
acc.updated(date, acc.getOrElse(date, Map.empty).updated(action, nb))
_.foldLeft(Map.empty[String, Row]) { case (acc, (date, key, nb)) =>
acc.updated(
date, {
val row = acc.getOrElse(date, Row(Map.empty, Map.empty))
Room.byKey
.get(key)
.map(row.add(_, nb))
.orElse(Action.dbMap.get(key).map(row.add(_, nb)))
.getOrElse(row)
}
)
}
}
.map { data =>
Result(
period,
who,
data.toList.sortBy(_._1).reverse.flatMap { case (date, actions) =>
Try(dateFormat parseDateTime date).toOption map { _ -> Row(actions) }
data.toList.sortBy(_._1).reverse.flatMap { case (date, row) =>
Try(dateFormat parseDateTime date).toOption map { _ -> row }
}
)
}
@ -78,7 +104,10 @@ object ModProgress {
data: List[(DateTime, Row)]
)
case class Row(actions: Map[Action, Int])
case class Row(actions: Map[Action, Int], reports: Map[Room, Int]) {
def add(action: Action, nb: Int) = copy(actions = actions.updatedWith(action)(prev => Some(~prev + nb)))
def add(room: Room, nb: Int) = copy(reports = reports.updatedWith(room)(prev => Some(~prev + nb)))
}
private val dateFormat = DateTimeFormat forPattern "yyyy-MM-dd"
@ -107,7 +136,6 @@ object ModProgress {
case object MarkTroll extends Action
case object MarkBoost extends Action
case object CloseAccount extends Action
case object CloseTeam extends Action
case object ChatTimeout extends Action
case object Appeal extends Action
case object SetEmail extends Action
@ -125,8 +153,6 @@ object ModProgress {
"unalt" -> CloseAccount,
"closeAccount" -> CloseAccount,
"reopenAccount" -> CloseAccount,
"disableTeam" -> CloseTeam,
"enableTeam" -> CloseTeam,
"chatTimeout" -> ChatTimeout,
"appealPost" -> Appeal,
"appealClose" -> Appeal,

View File

@ -8,8 +8,9 @@ object BSONHandlers {
implicit val ReasonBSONHandler = isoHandler[Reason, String](Reason.reasonIso)
implicit val RoomBSONHandler = isoHandler[Room, String](Room.roomIso)
import Report.{ Atom, Inquiry, Score }
import Report.{ Atom, Done, Inquiry, Score }
implicit val InquiryBSONHandler = Macros.handler[Inquiry]
implicit val DoneBSONHandler = Macros.handler[Done]
implicit val ReporterIdBSONHandler = stringIsoHandler[ReporterId](ReporterId.reporterIdIso)
implicit val ScoreIdBSONHandler = doubleIsoHandler[Score](Report.scoreIso)
implicit val AtomBSONHandler = Macros.handler[Atom]

View File

@ -14,7 +14,7 @@ case class Report(
score: Report.Score,
inquiry: Option[Report.Inquiry],
open: Boolean,
processedBy: Option[User.ID]
done: Option[Report.Done]
) extends Reason.WithReason {
import Report.{ Atom, Score }
@ -67,7 +67,7 @@ case class Report(
def process(by: User) =
copy(
open = false,
processedBy = by.id.some
done = Report.Done(by.id, DateTime.now).some
)
def userIds: List[User.ID] = user :: atoms.toList.map(_.by.value)
@ -106,6 +106,8 @@ object Report {
def byLichess = by == ReporterId.lichess
}
case class Done(by: User.ID, at: DateTime)
case class Inquiry(mod: User.ID, seenAt: DateTime)
case class WithSuspect(report: Report, suspect: Suspect, isOnline: Boolean) {
@ -163,7 +165,7 @@ object Report {
score = score,
inquiry = none,
open = true,
processedBy = none
done = none
)
)(_ add c.atom)
}

View File

@ -229,12 +229,12 @@ final class ReportApi(
def reopenReports(suspect: Suspect): Funit =
for {
all <- recent(suspect, 10)
closed = all.filter(_.processedBy has ModId.lichess.value)
closed = all.filter(_.done.map(_.by) has ModId.lichess.value)
_ <-
coll.update
.one(
$inIds(closed.map(_.id)),
$set("open" -> true) ++ $unset("processedBy"),
$set("open" -> true) ++ $unset("done"),
multi = true
)
.void
@ -306,8 +306,8 @@ final class ReportApi(
.one(
selector,
$set(
"open" -> false,
"processedBy" -> by.value
"open" -> false,
"done" -> Report.Done(by.value, DateTime.now)
) ++ $unset("inquiry"),
multi = true
)
@ -598,7 +598,7 @@ final class ReportApi(
coll.update
.one(
$id(report.id),
$unset("inquiry", "processedBy") ++ $set("open" -> true)
$unset("inquiry", "done") ++ $set("open" -> true)
)
.void