Big upheaval of how the reports system works

* There are now Reports and ReportRecords.
* Reports coordinate to what moderators see, and ReportRecords
  coordinate with the reports that are created by individual users.
* Reports keep track of the report reason and the creating user.
* ReportRecords keep track of the total weight and whether this report
  requires urgent attention or not.
* ReportRecord keeps track of its own weight and urgency because then we
  can sort by weight and urgency in the admin view.

Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
2022-06-19 22:03:01 -07:00
parent 5742dc4dc2
commit 6bda7f4f2d
5 changed files with 85 additions and 13 deletions

View File

@@ -1,6 +1,6 @@
from django.contrib import admin
from django.utils.safestring import mark_safe
from board.models import Board, Post, ReportReason, Report
from board.models import Board, Post, ReportReason, ReportRecord
@admin.register(Board)
@@ -18,9 +18,13 @@ class ReportReasonAdmin(admin.ModelAdmin):
pass
@admin.register(Report)
class ReportAdmin(admin.ModelAdmin):
readonly_fields = ("post", "reason", "ip")
@admin.register(ReportRecord)
class ReportRecordAdmin(admin.ModelAdmin):
ordering = (
"-urgent",
"-weight",
)
readonly_fields = ("post",)
list_display = ("post_thumbnail", "post_body")
save_as = False
@@ -32,7 +36,12 @@ class ReportAdmin(admin.ModelAdmin):
def post_body(self, obj):
html = ""
if obj.urgent:
html += '<div class="urgent">'
else:
html += "<div>"
if obj.post.subject:
html += f"<strong>{obj.post.subject}</strong>"
html += f"<p>{obj.post.text}</p>"
html += "</div>"
return mark_safe(html)

View File

@@ -1,6 +1,7 @@
from django.conf import settings
from django.db import transaction
from django.forms import ModelForm
from board.models import Post, Report
from board.models import Post, Report, ReportRecord
from hcaptcha.fields import hCaptchaField
@@ -50,6 +51,8 @@ class ReportForm(ModelForm):
This requires the board and the IP address to be specified.
"""
# uses report_form.html
class Meta:
model = Report
fields = ["reason"]
@@ -57,4 +60,14 @@ class ReportForm(ModelForm):
def __init__(self, *args, op, board, ip, **kwargs):
super(ReportForm, self).__init__(*args, **kwargs)
self.instance.ip = ip
self.instance.post = op
self.op = op
def clean(self):
# Get or create the record before creating the model
with transaction.atomic():
try:
record = ReportRecord.objects.get(post=self.op)
except ReportRecord.DoesNotExist:
record = ReportRecord.objects.create(post=self.op)
self.instance.record = record
return super(ReportForm, self).clean()

View File

@@ -1,11 +1,11 @@
from datetime import timedelta
import os
from pathlib import Path
from django.db import models
from django.db.models import signals
from django.conf import settings
from django.core.exceptions import ValidationError
from django.core.files.base import ContentFile
from django.db import models
from django.db.models import signals
from django.dispatch import receiver
from django.urls import reverse
from django.utils import timezone
@@ -218,15 +218,51 @@ class ReportReason(models.Model):
return self.reason
class Report(models.Model):
class ReportRecord(models.Model):
# Post that this report is for
post = models.ForeignKey("Post", on_delete=models.CASCADE)
post = models.OneToOneField("Post", on_delete=models.CASCADE)
# Report weight
weight = models.IntegerField(default=0)
# If this report is urgent or not
urgent = models.BooleanField(default=False)
# def urgent(self) -> bool:
# return self.report_set().aggregate(
class Report(models.Model):
"""
There are two reasons why this model exists:
1. In the report view in the Django admin, there is probably not a good way
to specially modify the list of reports. Since we don't want to see every
single report, we need a way to limit this *in data* rather than in code.
We accomplish this by having:
- Report model, which is all reports unique to users.
- ReportRecord model, which is the collection of all reports unique to posts.
2. We want to keep a log of all reports created by users, in case we think
they are abusing the system.
"""
# The report that was made by this user
record = models.ForeignKey("ReportRecord", on_delete=models.CASCADE)
# Reason for this report
reason = models.ForeignKey("ReportReason", on_delete=models.SET_NULL, null=True)
# IP address of the reporter
ip = models.GenericIPAddressField()
@receiver(signals.post_save, sender=Report)
def report_created(sender, instance, created, **kwargs):
if created:
# get the total weight, this is probably going to be more reliable than
# adding the weight every time
weights = [report.reason.weight for report in instance.record.report_set.all()]
instance.record.weight = sum(weights)
instance.record.urgent |= instance.reason.urgent
instance.record.save()
class Ban(models.Model):
# IP address of the reporter
ip = models.GenericIPAddressField()
@@ -239,7 +275,7 @@ class Ban(models.Model):
# The time that this ban was created.
created = models.DateTimeField(auto_now_add=True)
# Expiration date of this ban.
expires = models.DateTimeField(auto_now_add=True)
expires = models.DateTimeField()
class BanTemplate(models.Model):
@@ -249,4 +285,5 @@ class BanTemplate(models.Model):
duration = models.DurationField()
def create_ban(self, ip: str) -> Ban:
return Ban.objects.create(ip=ip, ban_reason=self.ban_reason)
expires = timezone.now() + self.duration
return Ban.objects.create(ip=ip, ban_reason=self.ban_reason, expires=expires)

View File

@@ -0,0 +1,13 @@
{% extends "admin/change_list.html" %}
{% block extrastyle %}
{{ block.super }}
<style>
.field-post_body {
width: 100%;
}
.urgent {
background-color: var(--message-error-bg);
color: var(--error-fg);
}
</style>
{% endblock extrastyle %}

View File

@@ -4,7 +4,7 @@ from django.shortcuts import render, get_object_or_404
from django.views.generic import DetailView
from django.views.generic.edit import CreateView
from django.urls import reverse, reverse_lazy
from board.models import Post, Board, Report
from board.models import Post, Board, Report, ReportRecord
from board.forms import PostForm, ReplyForm, ReportForm
__all__ = ("BoardView", "PostView", "ReportView")