diff --git a/bookwyrm/templates/settings/dashboard/dashboard.html b/bookwyrm/templates/settings/dashboard/dashboard.html
index cec3e89ac..46d552fb1 100644
--- a/bookwyrm/templates/settings/dashboard/dashboard.html
+++ b/bookwyrm/templates/settings/dashboard/dashboard.html
@@ -37,6 +37,17 @@
+ {% if email_config_error %}
+
+
+ {% blocktrans trimmed %}
+ Your outgoing email address, {{ email_sender }}
, may be misconfigured.
+ {% endblocktrans %}
+ {% trans "Check the EMAIL_SENDER_NAME
and EMAIL_SENDER_DOMAIN
in your .env
." %}
+
+
+ {% endif %}
+
{% if reports %}
diff --git a/bookwyrm/views/admin/dashboard.py b/bookwyrm/views/admin/dashboard.py
index b06b6ba00..df34fe5bf 100644
--- a/bookwyrm/views/admin/dashboard.py
+++ b/bookwyrm/views/admin/dashboard.py
@@ -1,5 +1,7 @@
""" instance overview """
from datetime import timedelta
+import re
+
from dateutil.parser import parse
from packaging import version
@@ -13,6 +15,7 @@ from django.views import View
from bookwyrm import models, settings
from bookwyrm.connectors.abstract_connector import get_data
from bookwyrm.connectors.connector_manager import ConnectorException
+from bookwyrm.utils import regex
# pylint: disable= no-self-use
@@ -26,90 +29,18 @@ class Dashboard(View):
def get(self, request):
"""list of users"""
- interval = int(request.GET.get("days", 1))
- now = timezone.now()
- start = request.GET.get("start")
- if start:
- start = timezone.make_aware(parse(start))
- else:
- start = now - timedelta(days=6 * interval)
+ data = get_charts_and_stats(request)
- end = request.GET.get("end")
- end = timezone.make_aware(parse(end)) if end else now
- start = start.replace(hour=0, minute=0, second=0)
+ # Make sure email looks properly configured
+ email_config_error = re.findall(
+ r"[\s\@]", settings.EMAIL_SENDER_DOMAIN
+ ) or not re.match(regex.DOMAIN, settings.EMAIL_SENDER_DOMAIN)
- user_queryset = models.User.objects.filter(local=True)
- user_chart = Chart(
- queryset=user_queryset,
- queries={
- "total": lambda q, s, e: q.filter(
- Q(is_active=True) | Q(deactivation_date__gt=e),
- created_date__lte=e,
- ).count(),
- "active": lambda q, s, e: q.filter(
- Q(is_active=True) | Q(deactivation_date__gt=e),
- created_date__lte=e,
- )
- .filter(
- last_active_date__gt=e - timedelta(days=31),
- )
- .count(),
- },
- )
-
- status_queryset = models.Status.objects.filter(user__local=True, deleted=False)
- status_chart = Chart(
- queryset=status_queryset,
- queries={
- "total": lambda q, s, e: q.filter(
- created_date__gt=s,
- created_date__lte=e,
- ).count()
- },
- )
-
- register_chart = Chart(
- queryset=user_queryset,
- queries={
- "total": lambda q, s, e: q.filter(
- created_date__gt=s,
- created_date__lte=e,
- ).count()
- },
- )
-
- works_chart = Chart(
- queryset=models.Work.objects,
- queries={
- "total": lambda q, s, e: q.filter(
- created_date__gt=s,
- created_date__lte=e,
- ).count()
- },
- )
-
- data = {
- "start": start.strftime("%Y-%m-%d"),
- "end": end.strftime("%Y-%m-%d"),
- "interval": interval,
- "users": user_queryset.filter(is_active=True).count(),
- "active_users": user_queryset.filter(
- is_active=True, last_active_date__gte=now - timedelta(days=31)
- ).count(),
- "statuses": status_queryset.count(),
- "works": models.Work.objects.count(),
- "reports": models.Report.objects.filter(resolved=False).count(),
- "pending_domains": models.LinkDomain.objects.filter(
- status="pending"
- ).count(),
- "invite_requests": models.InviteRequest.objects.filter(
- ignored=False, invite__isnull=True
- ).count(),
- "user_stats": user_chart.get_chart(start, end, interval),
- "status_stats": status_chart.get_chart(start, end, interval),
- "register_stats": register_chart.get_chart(start, end, interval),
- "works_stats": works_chart.get_chart(start, end, interval),
- }
+ data["email_config_error"] = email_config_error
+ # pylint: disable=line-too-long
+ data[
+ "email_sender"
+ ] = f"{settings.EMAIL_SENDER_NAME}@{settings.EMAIL_SENDER_DOMAIN}"
# check version
try:
@@ -126,6 +57,91 @@ class Dashboard(View):
return TemplateResponse(request, "settings/dashboard/dashboard.html", data)
+def get_charts_and_stats(request):
+ """Defines the dashbaord charts"""
+ interval = int(request.GET.get("days", 1))
+ now = timezone.now()
+ start = request.GET.get("start")
+ if start:
+ start = timezone.make_aware(parse(start))
+ else:
+ start = now - timedelta(days=6 * interval)
+
+ end = request.GET.get("end")
+ end = timezone.make_aware(parse(end)) if end else now
+ start = start.replace(hour=0, minute=0, second=0)
+
+ user_queryset = models.User.objects.filter(local=True)
+ user_chart = Chart(
+ queryset=user_queryset,
+ queries={
+ "total": lambda q, s, e: q.filter(
+ Q(is_active=True) | Q(deactivation_date__gt=e),
+ created_date__lte=e,
+ ).count(),
+ "active": lambda q, s, e: q.filter(
+ Q(is_active=True) | Q(deactivation_date__gt=e),
+ created_date__lte=e,
+ )
+ .filter(
+ last_active_date__gt=e - timedelta(days=31),
+ )
+ .count(),
+ },
+ )
+
+ status_queryset = models.Status.objects.filter(user__local=True, deleted=False)
+ status_chart = Chart(
+ queryset=status_queryset,
+ queries={
+ "total": lambda q, s, e: q.filter(
+ created_date__gt=s,
+ created_date__lte=e,
+ ).count()
+ },
+ )
+
+ register_chart = Chart(
+ queryset=user_queryset,
+ queries={
+ "total": lambda q, s, e: q.filter(
+ created_date__gt=s,
+ created_date__lte=e,
+ ).count()
+ },
+ )
+
+ works_chart = Chart(
+ queryset=models.Work.objects,
+ queries={
+ "total": lambda q, s, e: q.filter(
+ created_date__gt=s,
+ created_date__lte=e,
+ ).count()
+ },
+ )
+ return {
+ "start": start.strftime("%Y-%m-%d"),
+ "end": end.strftime("%Y-%m-%d"),
+ "interval": interval,
+ "users": user_queryset.filter(is_active=True).count(),
+ "active_users": user_queryset.filter(
+ is_active=True, last_active_date__gte=now - timedelta(days=31)
+ ).count(),
+ "statuses": status_queryset.count(),
+ "works": models.Work.objects.count(),
+ "reports": models.Report.objects.filter(resolved=False).count(),
+ "pending_domains": models.LinkDomain.objects.filter(status="pending").count(),
+ "invite_requests": models.InviteRequest.objects.filter(
+ ignored=False, invite__isnull=True
+ ).count(),
+ "user_stats": user_chart.get_chart(start, end, interval),
+ "status_stats": status_chart.get_chart(start, end, interval),
+ "register_stats": register_chart.get_chart(start, end, interval),
+ "works_stats": works_chart.get_chart(start, end, interval),
+ }
+
+
class Chart:
"""Data for a chart"""