diff --git a/alerts/tasks.py b/alerts/tasks.py
index c8c09a2..c95f8a8 100644
--- a/alerts/tasks.py
+++ b/alerts/tasks.py
@@ -1,7 +1,9 @@
from celery import shared_task
+from django.conf import settings
+from django.template.defaultfilters import truncatechars
+
from projects.models import ProjectMembership
-from issues.models import Issue
from .utils import send_rendered_email
@@ -13,24 +15,36 @@ def _get_users_for_email_alert(issue):
@shared_task
def send_new_issue_alert(issue_id):
- issue = Issue.objects.get(id=issue_id)
- for membership in _get_users_for_email_alert(issue):
- send_rendered_email(
- subject=f"New issue: {issue.title()}",
- base_template_name="alerts/new_issue",
- recipient_list=[membership.user.email],
- context={
- "issue": issue,
- "project": issue.project,
- },
- )
+ _send_alert(issue_id, "New issue:", "a", "NEW")
@shared_task
def send_regression_alert(issue_id):
- raise NotImplementedError("TODO")
+ _send_alert(issue_id, "Regression:", "a", "REGRESSED")
@shared_task
def send_unmute_alert(issue_id):
- raise NotImplementedError("TODO")
+ _send_alert(issue_id, "Unmuted issue:", "an", "UNMUTED")
+
+
+def _send_alert(issue_id, subject_prefix, alert_article, alert_reason):
+ from issues.models import Issue # avoid circular import
+
+ issue = Issue.objects.get(id=issue_id)
+ for membership in _get_users_for_email_alert(issue):
+ send_rendered_email(
+ subject=truncatechars(f"{subject_prefix} {issue.title()} in {issue.project.name}", 100),
+ base_template_name="alerts/issue_alert",
+ recipient_list=[membership.user.email],
+ context={
+ "site_name": settings.SITE_NAME,
+ "base_url": settings.BASE_URL + "/",
+ "issue_title": issue.title(),
+ "project_name": issue.project.name,
+ "issue_url": settings.BASE_URL + issue.get_absolute_url(),
+ "alert_article": alert_article,
+ "alert_reason": alert_reason,
+ "settings_url": settings.BASE_URL + "/", # TODO
+ },
+ )
diff --git a/alerts/templates/alerts/new_issue.html b/alerts/templates/alerts/issue_alert.html
similarity index 96%
rename from alerts/templates/alerts/new_issue.html
rename to alerts/templates/alerts/issue_alert.html
index fd5b920..704e94c 100644
--- a/alerts/templates/alerts/new_issue.html
+++ b/alerts/templates/alerts/issue_alert.html
@@ -462,7 +462,7 @@
|
- Bugsink
+ {{ site_name }}
|
@@ -470,14 +470,14 @@
- {% Body content #}
+ {# Body content #}
- {{ issue_title }}
+ {{ issue_title|truncatechars:100 }}
- This is a NEW issue on project "{{ project_name }}".
+ This is {{ alert_article }} {{ alert_reason }} issue on project "{{ project_name }}".
{# Action #}
@@ -488,19 +488,22 @@
|
+
+
{# Sub copy #}
|
- If you’re having trouble with the button above, copy and paste the URL below into your web browser.
- {{ issue_url }}
+ Copy/paste link:
+ {{ issue_url }}
+ Manage notification settings
|
diff --git a/alerts/tests.py b/alerts/tests.py
index 7ce503c..51a4e44 100644
--- a/alerts/tests.py
+++ b/alerts/tests.py
@@ -1,3 +1,83 @@
from django.test import TestCase
-# Create your tests here.
+from django.core import mail
+from django.contrib.auth.models import User
+from django.template.loader import get_template
+
+from issues.factories import get_or_create_issue
+from projects.models import Project, ProjectMembership
+from events.factories import create_event
+
+from .tasks import send_new_issue_alert, send_regression_alert, send_unmute_alert
+from .views import DEBUG_CONTEXTS
+
+
+class TestAlertSending(TestCase):
+
+ def test_send_new_issue_alert(self):
+ project = Project.objects.create(name="Test project")
+
+ user = User.objects.create_user(username="testuser", email="test@example.org")
+ ProjectMembership.objects.create(
+ project=project,
+ user=user,
+ send_email_alerts=True,
+ )
+
+ issue, _ = get_or_create_issue(project=project)
+ create_event(project=project, issue=issue)
+
+ send_new_issue_alert(issue.id)
+
+ self.assertEqual(len(mail.outbox), 1)
+
+ def test_send_regression_alert(self):
+ project = Project.objects.create(name="Test project")
+
+ user = User.objects.create_user(username="testuser", email="test@example.org")
+ ProjectMembership.objects.create(
+ project=project,
+ user=user,
+ send_email_alerts=True,
+ )
+
+ issue, _ = get_or_create_issue(project=project)
+ create_event(project=project, issue=issue)
+
+ send_regression_alert(issue.id)
+
+ self.assertEqual(len(mail.outbox), 1)
+
+ def test_send_unmute_alert(self):
+ project = Project.objects.create(name="Test project")
+
+ user = User.objects.create_user(username="testuser", email="test@example.org")
+ ProjectMembership.objects.create(
+ project=project,
+ user=user,
+ send_email_alerts=True,
+ )
+
+ issue, _ = get_or_create_issue(project=project)
+ create_event(project=project, issue=issue)
+
+ send_unmute_alert(issue.id)
+
+ self.assertEqual(len(mail.outbox), 1)
+
+ def test_txt_and_html_have_relevant_variables_defined(self):
+ example_context = DEBUG_CONTEXTS["issue_alert"]
+ html_template = get_template("alerts/issue_alert.html")
+ text_template = get_template("alerts/issue_alert.txt")
+
+ unused_in_text = [
+ "base_url", # link to the site is not included at the top of the text template
+ ]
+
+ for type_, template in [("html", html_template), ("text", text_template)]:
+ for variable in example_context.keys():
+ if type_ == "text" and variable in unused_in_text:
+ continue
+
+ self.assertTrue(
+ "{{ %s" % variable in template.template.source, "'{{ %s ' not in %s template" % (variable, type_))
diff --git a/alerts/utils.py b/alerts/utils.py
index 3fa7fd7..b3c8918 100644
--- a/alerts/utils.py
+++ b/alerts/utils.py
@@ -1,5 +1,4 @@
from django.core.mail import EmailMultiAlternatives
-from django.template import Context
from django.template.loader import get_template
@@ -7,8 +6,8 @@ def send_rendered_email(subject, base_template_name, recipient_list, context=Non
if context is None:
context = {}
- html_content = get_template(base_template_name + ".html").render(Context(context))
- text_content = get_template(base_template_name + ".txt").render(Context(context))
+ html_content = get_template(base_template_name + ".html").render(context)
+ text_content = get_template(base_template_name + ".txt").render(context)
# Configure and send an EmailMultiAlternatives
msg = EmailMultiAlternatives(
diff --git a/alerts/views.py b/alerts/views.py
index 81d64a8..b7c3ded 100644
--- a/alerts/views.py
+++ b/alerts/views.py
@@ -2,11 +2,15 @@ from django.shortcuts import render
from django.conf import settings
DEBUG_CONTEXTS = {
- "new_issue": {
+ "issue_alert": {
+ "site_name": settings.SITE_NAME,
"base_url": settings.BASE_URL + "/",
"issue_title": "AttributeError: 'NoneType' object has no attribute 'data'",
"project_name": "My first project",
+ "alert_article": "a",
+ "alert_reason": "NEW",
"issue_url": settings.BASE_URL + "/issues/issue/00000000-0000-0000-0000-000000000000/",
+ "settings_url": settings.BASE_URL + "/", # TODO
},
}
diff --git a/bugsink/settings.py b/bugsink/settings.py
index 99e4498..228d4f2 100644
--- a/bugsink/settings.py
+++ b/bugsink/settings.py
@@ -164,6 +164,7 @@ if SENTRY_DSN is not None:
)
BASE_URL = "http://bugsink:9000" # no trailing slash
+SITE_NAME = "Bugsink" # you can customize this as e.g. "My Bugsink" or "Bugsink for My Company"
CELERY_BROKER_URL = 'amqp://bugsink:bugsink@localhost/'
diff --git a/issues/factories.py b/issues/factories.py
index fbdfaea..b6bc014 100644
--- a/issues/factories.py
+++ b/issues/factories.py
@@ -2,12 +2,19 @@ import uuid
from django.utils import timezone
+from projects.models import Project
+
from .models import Issue
from .utils import get_hash_for_data
-def get_or_create_issue(project, event_data):
- # create issue for testing purposes (code basically stolen from ingest/views.py)
+def get_or_create_issue(project=None, event_data=None):
+ """create issue for testing purposes (code basically stolen from ingest/views.py)"""
+ if event_data is None:
+ event_data = create_event_data()
+ if project is None:
+ project = Project.objects.create(name="Test project")
+
hash_ = get_hash_for_data(event_data)
issue, issue_created = Issue.objects.get_or_create(
project=project,
@@ -17,7 +24,7 @@ def get_or_create_issue(project, event_data):
def create_event_data():
- # create minimal event data that is valid as per from_json()
+ """create minimal event data that is valid as per from_json()"""
return {
"event_id": uuid.uuid4().hex,
diff --git a/issues/models.py b/issues/models.py
index 51fdcc4..10db238 100644
--- a/issues/models.py
+++ b/issues/models.py
@@ -5,6 +5,7 @@ import uuid
from django.db import models
from bugsink.volume_based_condition import VolumeBasedCondition
+from alerts.tasks import send_unmute_alert
class Issue(models.Model):
@@ -129,7 +130,6 @@ class IssueStateManager(object):
# methods in this class.
from bugsink.registry import get_pc_registry, UNMUTE_PURPOSE # avoid circular import
- from alerts.tasks import send_unmute_alert
if issue.is_muted:
# we check on is_muted explicitly: it may be so that multiple unmute conditions happens simultaneously (and
|