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:
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
13
board/templates/admin/board/reportrecord/change_list.html
Normal file
13
board/templates/admin/board/reportrecord/change_list.html
Normal 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 %}
|
||||
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user