diff --git a/bookwyrm/forms.py b/bookwyrm/forms.py index 7ae4e446f..00e6d5d8c 100644 --- a/bookwyrm/forms.py +++ b/bookwyrm/forms.py @@ -8,6 +8,7 @@ from django.forms import ModelForm, PasswordInput, widgets, ChoiceField from django.forms.widgets import Textarea from django.utils import timezone from django.utils.translation import gettext_lazy as _ +from django_celery_beat.models import IntervalSchedule from bookwyrm import models from bookwyrm.models.fields import ClearableFileInputWithWarning @@ -556,3 +557,9 @@ class AutoModRuleForm(CustomForm): class Meta: model = models.AutoMod fields = ["string_match", "flag_users", "flag_statuses", "created_by"] + + +class IntervalScheduleForm(CustomForm): + class Meta: + model = IntervalSchedule + fields = ["every", "period"] diff --git a/bookwyrm/models/antispam.py b/bookwyrm/models/antispam.py index f506b6f19..bce02780d 100644 --- a/bookwyrm/models/antispam.py +++ b/bookwyrm/models/antispam.py @@ -54,6 +54,7 @@ class AutoMod(models.Model): @app.task(queue="low_priority") def automod_task(): """Create reports""" + print("TASK!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!") if not AutoMod.objects.exists(): return reporter = AutoMod.objects.first().created_by diff --git a/bookwyrm/settings.py b/bookwyrm/settings.py index 5d4eb7812..16e5ccb30 100644 --- a/bookwyrm/settings.py +++ b/bookwyrm/settings.py @@ -90,7 +90,7 @@ INSTALLED_APPS = [ "sass_processor", "bookwyrm", "celery", - 'django_celery_beat', + "django_celery_beat", "imagekit", "storages", ] diff --git a/bookwyrm/templates/settings/automod/rules.html b/bookwyrm/templates/settings/automod/rules.html index 8205b3d71..2a28cb641 100644 --- a/bookwyrm/templates/settings/automod/rules.html +++ b/bookwyrm/templates/settings/automod/rules.html @@ -1,5 +1,6 @@ {% extends 'settings/layout.html' %} {% load i18n %} +{% load humanize %} {% load utilities %} {% block title %} @@ -16,12 +17,46 @@

{% trans "Auto-moderation rules will create reports for any local user or status with fields matching the provided string." %} {% trans "Users or statuses that have already been reported (regardless of whether the report was resolved) will not be flagged." %} - {% trans "At this time, reports are not being generated automatically, and you must manually trigger a scan." %}

-
+ {% if task %} +
+
+ {% trans "Schedule:" %} +
+
+ {{ task.schedule }} +
+ +
+ {% trans "Last run:" %} +
+
+ {{ task.last_run_at|naturaltime }} +
+ +
+ {% trans "Total run count:" %} +
+
+ {{ task.total_run_count }} +
+ +
+ {% trans "Enabled:" %} +
+
+ + {{ task.enabled|yesno }} + +
+
+ {% else %} + {% csrf_token %} - + {{ task_form.as_p }} +
+ {% endif %} {% if success %} diff --git a/bookwyrm/urls.py b/bookwyrm/urls.py index d2caa76ea..5abe7ac2e 100644 --- a/bookwyrm/urls.py +++ b/bookwyrm/urls.py @@ -217,11 +217,18 @@ urlpatterns = [ # auto-moderation rules re_path(r"^settings/automod/?$", views.AutoMod.as_view(), name="settings-automod"), re_path( - r"^settings/automod/(?P\d+)/delete?$", + r"^settings/automod/(?P\d+)/delete/?$", views.automod_delete, name="settings-automod-delete", ), - re_path(r"^settings/automod/run?$", views.run_automod, name="settings-automod-run"), + re_path( + r"^settings/automod/schedule/?$", + views.schedule_automod_task, + name="settings-automod-schedule", + ), + re_path( + r"^settings/automod/run/?$", views.run_automod, name="settings-automod-run" + ), # moderation re_path( r"^settings/reports/?$", views.ReportsAdmin.as_view(), name="settings-reports" diff --git a/bookwyrm/views/__init__.py b/bookwyrm/views/__init__.py index 76e9ff024..aa4d72999 100644 --- a/bookwyrm/views/__init__.py +++ b/bookwyrm/views/__init__.py @@ -2,7 +2,7 @@ # site admin from .admin.announcements import Announcements, Announcement from .admin.announcements import EditAnnouncement, delete_announcement -from .admin.automod import AutoMod, automod_delete, run_automod +from .admin.automod import AutoMod, automod_delete, run_automod, schedule_automod_task from .admin.dashboard import Dashboard from .admin.federation import Federation, FederatedServer from .admin.federation import AddFederatedServer, ImportServerBlocklist diff --git a/bookwyrm/views/admin/automod.py b/bookwyrm/views/admin/automod.py index d9901d01c..fbe6408c6 100644 --- a/bookwyrm/views/admin/automod.py +++ b/bookwyrm/views/admin/automod.py @@ -1,10 +1,12 @@ """ moderation via flagged posts and users """ from django.contrib.auth.decorators import login_required, permission_required +from django.db import transaction from django.shortcuts import get_object_or_404, redirect from django.template.response import TemplateResponse from django.utils.decorators import method_decorator from django.views import View from django.views.decorators.http import require_POST +from django_celery_beat.models import PeriodicTask from bookwyrm import forms, models @@ -24,8 +26,9 @@ class AutoMod(View): def get(self, request): """view rules""" - data = {"rules": models.AutoMod.objects.all(), "form": forms.AutoModRuleForm()} - return TemplateResponse(request, "settings/automod/rules.html", data) + return TemplateResponse( + request, "settings/automod/rules.html", automod_view_data() + ) def post(self, request): """add rule""" @@ -35,14 +38,32 @@ class AutoMod(View): form.save() form = forms.AutoModRuleForm() - data = { - "rules": models.AutoMod.objects.all(), - "form": form, - "success": success, - } + data = automod_view_data() + data["form"] = form return TemplateResponse(request, "settings/automod/rules.html", data) +@require_POST +@permission_required("bookwyrm.moderate_user", raise_exception=True) +@permission_required("bookwyrm.moderate_post", raise_exception=True) +def schedule_automod_task(request): + """scheduler""" + form = forms.IntervalScheduleForm(request.POST) + if not form.is_valid(): + data = automod_view_data() + data["task_form"] = form + return TemplateResponse(request, "settings/automod/rules.html", data) + + with transaction.atomic(): + schedule = form.save() + PeriodicTask.objects.get_or_create( + interval=schedule, + name="automod-task", + task="bookwyrm.models.antispam.automod_task", + ) + return redirect("settings-automod") + + @require_POST @permission_required("bookwyrm.moderate_user", raise_exception=True) @permission_required("bookwyrm.moderate_post", raise_exception=True) @@ -62,3 +83,18 @@ def run_automod(request): """run scan""" models.automod_task.delay() return redirect("settings-automod") + + +def automod_view_data(): + """helper to get data used in the template""" + try: + task = PeriodicTask.objects.get(name="automod-task") + except PeriodicTask.DoesNotExist: + task = None + + return { + "task": task, + "task_form": forms.IntervalScheduleForm(), + "rules": models.AutoMod.objects.all(), + "form": forms.AutoModRuleForm(), + } diff --git a/celerywyrm/settings.py b/celerywyrm/settings.py index bd7805e51..35eb3933f 100644 --- a/celerywyrm/settings.py +++ b/celerywyrm/settings.py @@ -3,6 +3,7 @@ # pylint: disable=unused-wildcard-import from bookwyrm.settings import * +# pylint: disable=line-too-long REDIS_BROKER_PASSWORD = requests.utils.quote(env("REDIS_BROKER_PASSWORD", None)) REDIS_BROKER_HOST = env("REDIS_BROKER_HOST", "redis_broker") REDIS_BROKER_PORT = env("REDIS_BROKER_PORT", 6379) @@ -16,6 +17,10 @@ CELERY_DEFAULT_QUEUE = "low_priority" CELERY_ACCEPT_CONTENT = ["json"] CELERY_TASK_SERIALIZER = "json" CELERY_RESULT_SERIALIZER = "json" + +CELERY_BEAT_SCHEDULER = "django_celery_beat.schedulers:DatabaseScheduler" +CELERY_TIMEZONE = env("TIME_ZONE", "UTC") + FLOWER_PORT = env("FLOWER_PORT") INSTALLED_APPS = INSTALLED_APPS + [