~crocmagnon/blog

f6d6d7b850df58a2f5ce8e8555f9b0328462224d — Gabriel Augendre a month ago 23985a3
Add task to notify commenters once their comment has been moderated.
M articles/admin.py => articles/admin.py +9 -1
@@ 116,7 116,15 @@ class PageAdmin(ArticleAdmin):

@register(Comment)
class CommentAdmin(admin.ModelAdmin):
    list_display = ("username", "email", "content", "article", "created_at", "status")
    list_display = (
        "username",
        "email",
        "content",
        "article",
        "created_at",
        "status",
        "user_notified",
    )
    list_filter = ("status",)
    search_fields = ("username", "email", "content")
    actions = ["approve_comments", "reject_comments"]

A articles/management/commands/notify_commenters.py => articles/management/commands/notify_commenters.py +54 -0
@@ 0,0 1,54 @@
from collections import defaultdict

from django.conf import settings
from django.core.mail import mail_admins, send_mass_mail
from django.core.management import BaseCommand
from django.db.models import Q
from django.template import Context, Engine
from django.template.loader import render_to_string
from django.urls import reverse
from django.utils.translation import ngettext

from articles.models import Comment


class Command(BaseCommand):
    help = "Check for pending comments and send an email to the admin."

    def handle(self, *args, **options):
        to_notify = (
            Comment.objects.filter(
                Q(status=Comment.APPROVED) | Q(status=Comment.REJECTED),
                user_notified=False,
            )
            .exclude(email=None)
            .exclude(email="")
        )
        by_email = {}
        for comment in to_notify:
            if comment.email not in by_email:
                by_email[comment.email] = {"approved": [], "rejected": []}
            if comment.status == Comment.APPROVED:
                by_email[comment.email]["approved"].append(comment)
            elif comment.status == Comment.REJECTED:
                by_email[comment.email]["rejected"].append(comment)

        email_data = []
        for email, comments in by_email.items():
            approved = comments["approved"]
            rejected = comments["rejected"]
            subject = ngettext(
                "Your comment has been moderated.",
                "Your comments have been moderated.",
                len(approved) + len(rejected),
            )
            blog_title = settings.BLOG["title"]
            message = render_to_string(
                "articles/comments_notification_email.txt",
                {"approved": approved, "rejected": rejected, "blog_title": blog_title},
            ).replace("'", "'")
            from_email = settings.DEFAULT_FROM_EMAIL
            recipient_list = [email]
            email_data.append((subject, message, from_email, recipient_list))
        send_mass_mail(tuple(email_data))
        to_notify.update(user_notified=True)

A articles/migrations/0016_comment_user_notified.py => articles/migrations/0016_comment_user_notified.py +18 -0
@@ 0,0 1,18 @@
# Generated by Django 3.1 on 2020-08-20 13:42

from django.db import migrations, models


class Migration(migrations.Migration):

    dependencies = [
        ("articles", "0015_auto_20200818_2138"),
    ]

    operations = [
        migrations.AddField(
            model_name="comment",
            name="user_notified",
            field=models.BooleanField(default=False),
        ),
    ]

A articles/migrations/0017_auto_20200820_1606.py => articles/migrations/0017_auto_20200820_1606.py +16 -0
@@ 0,0 1,16 @@
# Generated by Django 3.1 on 2020-08-20 14:06

from django.db import migrations


class Migration(migrations.Migration):

    dependencies = [
        ("articles", "0016_comment_user_notified"),
    ]

    operations = [
        migrations.AlterModelOptions(
            name="comment", options={"ordering": ["-created_at"]},
        ),
    ]

M articles/models.py => articles/models.py +14 -1
@@ 1,9 1,11 @@
import re

import markdown
from django.conf import settings
from django.contrib.auth.models import AbstractUser
from django.contrib.contenttypes.models import ContentType
from django.db import models
from django.http import HttpRequest
from django.template.defaultfilters import slugify
from django.urls import reverse
from django.utils import timezone


@@ 64,6 66,13 @@ class Article(AdminUrlMixin, models.Model):
    def get_absolute_url(self):
        return reverse("article-detail", kwargs={"slug": self.slug})

    def get_full_absolute_url(self, request: HttpRequest = None):
        url = self.get_absolute_url()
        if request:
            return request.build_absolute_uri(url)
        else:
            return (settings.BLOG["base_url"] + url).replace("//", "/")

    def get_abstract(self):
        html = self.get_formatted_content()
        return html.split("<!--more-->")[0]


@@ 130,12 139,16 @@ class Comment(AdminUrlMixin, models.Model):
    )
    created_at = models.DateTimeField(auto_now_add=True)
    status = models.CharField(max_length=10, choices=STATUS_CHOICES, default=PENDING)
    user_notified = models.BooleanField(default=False)

    class Meta:
        ordering = ["created_at"]
        ordering = ["-created_at"]

    def __str__(self):
        return f"{self.username} - {self.content[:50]}"

    def get_absolute_url(self):
        return self.article.get_absolute_url() + "#" + str(self.id)

    def get_full_absolute_url(self, request: HttpRequest = None):
        return self.article.get_full_absolute_url(request) + "#" + str(self.id)

A articles/templates/articles/comments_notification_email.txt => articles/templates/articles/comments_notification_email.txt +20 -0
@@ 0,0 1,20 @@
Hello,

This is a quick (automated) notification about the comments you left
on {{ blog_title }}.

{% if approved %}Approved:
{% for comment in approved %}* {{ comment.get_full_absolute_url }}
{% endfor %}{% endif %}

{% if rejected %}Rejected:
{% for comment in rejected %}* #{{comment.id}} on {{ comment.article.get_full_absolute_url }}:
{{ comment.content }}

{% endfor %}{% endif %}

You received this notification because you left your email address in the
comment form.

Cheers,
Gabriel

M blog/settings.py => blog/settings.py +1 -0
@@ 162,6 162,7 @@ else:
AUTH_USER_MODEL = "articles.User"

BLOG = {
    "title": "Gab's Notes",
    "description": "My take on tech-related subjects (but not only)",
    "base_url": os.getenv("BLOG_BASE_URL", "https://gabnotes.org/"),
}