from datetime import datetime, timedelta
from django.conf import settings
from django.core import mail
from django.core.management.base import BaseCommand
from django.db.models import Count, Q
from main import models
def build_summary_text(target_date: datetime.date) -> str:
prev_date = target_date - timedelta(days=1)
next_date = target_date + timedelta(days=1)
new_users_qs = models.User.objects.filter(date_joined__date=target_date).order_by(
"-id"
)
new_posts_qs = (
models.Post.objects.filter(created_at__date=target_date)
.select_related("owner")
.order_by("-created_at")
)
new_pages_qs = (
models.Page.objects.filter(created_at__date=target_date)
.select_related("owner")
.order_by("-created_at")
)
new_comments_qs = (
models.Comment.objects.filter(created_at__date=target_date)
.select_related("post", "post__owner")
.order_by("-created_at")
)
post_visits_count = models.AnalyticPost.objects.filter(
created_at__date=target_date
).count()
top_posts_by_visits_qs = (
models.Post.objects.filter(analyticpost__created_at__date=target_date)
.annotate(
visit_count=Count(
"analyticpost", filter=Q(analyticpost__created_at__date=target_date)
)
)
.select_related("owner")
.order_by("-visit_count", "-id")
)
lines: list[str] = []
lines.append(f"Moderation — Summary {target_date.strftime('%Y-%m-%d')}")
lines.append("")
lines.append("Counts")
lines.append(f"- New users: {new_users_qs.count()}")
lines.append(f"- New posts: {new_posts_qs.count()}")
lines.append(f"- New pages: {new_pages_qs.count()}")
lines.append(f"- New comments: {new_comments_qs.count()}")
lines.append(f"- Post visits: {post_visits_count}")
lines.append("")
lines.append("Top Posts by Visits")
if top_posts_by_visits_qs.exists():
for post in top_posts_by_visits_qs[:20]:
lines.append(
f"- {post.title} — {post.visit_count} — {post.owner.username} — {post.get_proper_url}"
)
else:
lines.append("- None.")
lines.append("")
lines.append("New Posts")
if new_posts_qs.exists():
for p in new_posts_qs:
lines.append(
f"- {p.title} by {p.owner.username} ({p.created_at.strftime('%H:%M')}) — {p.get_proper_url}"
)
else:
lines.append("- None.")
lines.append("")
lines.append("New Users")
if new_users_qs.exists():
for u in new_users_qs:
lines.append(
f"- {u.username} ({u.date_joined.strftime('%H:%M')}) — {u.blog_url}"
)
else:
lines.append("- None.")
lines.append("")
lines.append("New Pages")
if new_pages_qs.exists():
for pg in new_pages_qs:
lines.append(
f"- {pg.title} by {pg.owner.username} ({pg.created_at.strftime('%H:%M')}) — {pg.get_absolute_url()}"
)
else:
lines.append("- None.")
lines.append("")
lines.append("New Comments")
if new_comments_qs.exists():
for c in new_comments_qs:
pending_note = " pending" if not c.is_approved else ""
lines.append(
f"- on {c.post.title} by {c.post.owner.username} ({c.created_at.strftime('%H:%M')}){pending_note} — {c.post.get_proper_url}#comment-{c.id}"
)
else:
lines.append("- None.")
lines.append("")
lines.append(f"Prev day: {prev_date.strftime('%Y-%m-%d')} | Next day: {next_date.strftime('%Y-%m-%d')}")
return "\n".join(lines)
class Command(BaseCommand):
help = "Email daily moderation summary to admins."
def handle(self, *args, **options):
# Run for the previous day
target_date = (datetime.utcnow().date()) - timedelta(days=1)
self.stdout.write(self.style.NOTICE(f"Building summary for {target_date}."))
body = build_summary_text(target_date)
subject = f"Mataroa moderation summary {target_date.isoformat()} — {settings.CANONICAL_HOST}"
to_addresses = [email for _name, email in settings.ADMINS]
if not to_addresses:
self.stdout.write(self.style.ERROR("No admin addresses configured (ADMINS)."))
return
email = mail.EmailMessage(
subject=subject,
body=body,
from_email=settings.DEFAULT_FROM_EMAIL,
to=to_addresses,
)
connection = mail.get_connection()
connection.send_messages([email])
self.stdout.write(self.style.SUCCESS("Daily moderation summary sent."))