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.contrib import admin
|
||||||
from django.utils.safestring import mark_safe
|
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)
|
@admin.register(Board)
|
||||||
@@ -18,9 +18,13 @@ class ReportReasonAdmin(admin.ModelAdmin):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Report)
|
@admin.register(ReportRecord)
|
||||||
class ReportAdmin(admin.ModelAdmin):
|
class ReportRecordAdmin(admin.ModelAdmin):
|
||||||
readonly_fields = ("post", "reason", "ip")
|
ordering = (
|
||||||
|
"-urgent",
|
||||||
|
"-weight",
|
||||||
|
)
|
||||||
|
readonly_fields = ("post",)
|
||||||
list_display = ("post_thumbnail", "post_body")
|
list_display = ("post_thumbnail", "post_body")
|
||||||
save_as = False
|
save_as = False
|
||||||
|
|
||||||
@@ -32,7 +36,12 @@ class ReportAdmin(admin.ModelAdmin):
|
|||||||
|
|
||||||
def post_body(self, obj):
|
def post_body(self, obj):
|
||||||
html = ""
|
html = ""
|
||||||
|
if obj.urgent:
|
||||||
|
html += '<div class="urgent">'
|
||||||
|
else:
|
||||||
|
html += "<div>"
|
||||||
if obj.post.subject:
|
if obj.post.subject:
|
||||||
html += f"<strong>{obj.post.subject}</strong>"
|
html += f"<strong>{obj.post.subject}</strong>"
|
||||||
html += f"<p>{obj.post.text}</p>"
|
html += f"<p>{obj.post.text}</p>"
|
||||||
|
html += "</div>"
|
||||||
return mark_safe(html)
|
return mark_safe(html)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.db import transaction
|
||||||
from django.forms import ModelForm
|
from django.forms import ModelForm
|
||||||
from board.models import Post, Report
|
from board.models import Post, Report, ReportRecord
|
||||||
from hcaptcha.fields import hCaptchaField
|
from hcaptcha.fields import hCaptchaField
|
||||||
|
|
||||||
|
|
||||||
@@ -50,6 +51,8 @@ class ReportForm(ModelForm):
|
|||||||
This requires the board and the IP address to be specified.
|
This requires the board and the IP address to be specified.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# uses report_form.html
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Report
|
model = Report
|
||||||
fields = ["reason"]
|
fields = ["reason"]
|
||||||
@@ -57,4 +60,14 @@ class ReportForm(ModelForm):
|
|||||||
def __init__(self, *args, op, board, ip, **kwargs):
|
def __init__(self, *args, op, board, ip, **kwargs):
|
||||||
super(ReportForm, self).__init__(*args, **kwargs)
|
super(ReportForm, self).__init__(*args, **kwargs)
|
||||||
self.instance.ip = ip
|
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
|
from datetime import timedelta
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from django.db import models
|
|
||||||
from django.db.models import signals
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.core.files.base import ContentFile
|
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.dispatch import receiver
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
@@ -218,15 +218,51 @@ class ReportReason(models.Model):
|
|||||||
return self.reason
|
return self.reason
|
||||||
|
|
||||||
|
|
||||||
class Report(models.Model):
|
class ReportRecord(models.Model):
|
||||||
# Post that this report is for
|
# 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 for this report
|
||||||
reason = models.ForeignKey("ReportReason", on_delete=models.SET_NULL, null=True)
|
reason = models.ForeignKey("ReportReason", on_delete=models.SET_NULL, null=True)
|
||||||
# IP address of the reporter
|
# IP address of the reporter
|
||||||
ip = models.GenericIPAddressField()
|
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):
|
class Ban(models.Model):
|
||||||
# IP address of the reporter
|
# IP address of the reporter
|
||||||
ip = models.GenericIPAddressField()
|
ip = models.GenericIPAddressField()
|
||||||
@@ -239,7 +275,7 @@ class Ban(models.Model):
|
|||||||
# The time that this ban was created.
|
# The time that this ban was created.
|
||||||
created = models.DateTimeField(auto_now_add=True)
|
created = models.DateTimeField(auto_now_add=True)
|
||||||
# Expiration date of this ban.
|
# Expiration date of this ban.
|
||||||
expires = models.DateTimeField(auto_now_add=True)
|
expires = models.DateTimeField()
|
||||||
|
|
||||||
|
|
||||||
class BanTemplate(models.Model):
|
class BanTemplate(models.Model):
|
||||||
@@ -249,4 +285,5 @@ class BanTemplate(models.Model):
|
|||||||
duration = models.DurationField()
|
duration = models.DurationField()
|
||||||
|
|
||||||
def create_ban(self, ip: str) -> Ban:
|
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 import DetailView
|
||||||
from django.views.generic.edit import CreateView
|
from django.views.generic.edit import CreateView
|
||||||
from django.urls import reverse, reverse_lazy
|
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
|
from board.forms import PostForm, ReplyForm, ReportForm
|
||||||
|
|
||||||
__all__ = ("BoardView", "PostView", "ReportView")
|
__all__ = ("BoardView", "PostView", "ReportView")
|
||||||
|
|||||||
Reference in New Issue
Block a user