Commit 6a9a5128 by Awais

Implement the emails for course run change states.

Adding templates and email functionality.

ECOM-6004
parent 75c3e54d
import logging
from django.conf import settings
from django.contrib.sites.models import Site
from django.core.mail.message import EmailMultiAlternatives
from django.core.urlresolvers import reverse
from django.template.loader import get_template
from django.utils.translation import ugettext_lazy as _
log = logging.getLogger(__name__)
def send_email_for_change_state(course_run):
""" Send the emails for a comment.
Arguments:
course_run (Object): CourseRun object
"""
try:
txt_template = 'publisher/email/change_state.txt'
html_template = 'publisher/email/change_state.html'
to_addresses = course_run.course.get_group_users_emails()
from_address = settings.PUBLISHER_FROM_EMAIL
page_path = reverse('publisher:publisher_course_run_detail', kwargs={'pk': course_run.id})
context = {
'state_name': course_run.current_state,
'course_run_page_url': 'https://{host}{path}'.format(
host=Site.objects.get_current().domain.strip('/'), path=page_path
)
}
template = get_template(txt_template)
plain_content = template.render(context)
template = get_template(html_template)
html_content = template.render(context)
subject = _('Course Run {title}-{pacing_type}-{start} state has been changed.').format(
title=course_run.course.title,
pacing_type=course_run.get_pacing_type_display(),
start=course_run.start.strftime('%B %d, %Y') if course_run.start else ''
)
email_msg = EmailMultiAlternatives(
subject, plain_content, from_address, to_addresses
)
email_msg.attach_alternative(html_content, 'text/html')
email_msg.send()
except Exception: # pylint: disable=broad-except
log.exception('Failed to send email notifications for change state of course-run %s', course_run.id)
import logging
from django.core.urlresolvers import reverse
from django.core.urlresolvers import reverse
from django.db import models
from django.db.models.signals import pre_save
from django.dispatch import receiver
......@@ -12,12 +12,14 @@ from simple_history.models import HistoricalRecords
from sortedm2m.fields import SortedManyToManyField
from stdimage.models import StdImageField
from taggit.managers import TaggableManager
import waffle
from course_discovery.apps.core.models import User, Currency
from course_discovery.apps.course_metadata.choices import CourseRunPacing
from course_discovery.apps.course_metadata.models import LevelType, Subject, Person, Organization
from course_discovery.apps.course_metadata.utils import UploadToFieldNamePath
from course_discovery.apps.ietf_language_tags.models import LanguageTag
from course_discovery.apps.publisher.emails import send_email_for_change_state
logger = logging.getLogger(__name__)
......@@ -277,6 +279,9 @@ class CourseRun(TimeStampedModel, ChangedByMixin):
self.state.save()
if waffle.switch_is_active('enable_publisher_email_notifications'):
send_email_for_change_state(self)
@property
def current_state(self):
return self.state.get_name_display()
......
# pylint: disable=no-member
import datetime
import ddt
from django.conf import settings
from django.contrib.sites.models import Site
from django.core.urlresolvers import reverse
from django.test import TestCase
from django.core import mail
from guardian.shortcuts import assign_perm
import pytz
import mock
from course_discovery.apps.core.tests.factories import UserFactory
from course_discovery.apps.course_metadata.tests import toggle_switch
from course_discovery.apps.publisher.models import State, Course
from course_discovery.apps.publisher.tests import factories
from course_discovery.apps.publisher.tests.factories import UserAttributeFactory
@ddt.ddt
class StateChangeEmailTests(TestCase):
""" Tests for the Email functionality for course run state changes. """
@classmethod
def setUpClass(cls):
super(StateChangeEmailTests, cls).setUpClass()
cls.user = UserFactory()
cls.user_2 = UserFactory()
cls.user_3 = UserFactory()
cls.site = Site.objects.get(pk=settings.SITE_ID)
cls.group = factories.GroupFactory()
cls.user.groups.add(cls.group)
cls.user_2.groups.add(cls.group)
cls.user_3.groups.add(cls.group)
cls.seat = factories.SeatFactory()
cls.course_run = cls.seat.course_run
cls.course = cls.course_run.course
assign_perm(Course.VIEW_PERMISSION, cls.group, cls.course)
# NOTE: We intentionally do NOT create an attribute for user_2.
# By default this user WILL receive email notifications.
UserAttributeFactory(user=cls.user, enable_email_notification=True)
UserAttributeFactory(user=cls.user_3, enable_email_notification=False)
toggle_switch('enable_publisher_email_notifications', True)
@mock.patch('course_discovery.apps.publisher.models.send_email_for_change_state')
def test_email_with_enable_waffle_switch(self, send_email_for_change_state):
""" Verify that send_email_for_state called with enable waffle switch.. """
self.course_run.change_state(target=State.DRAFT)
send_email_for_change_state.assert_called_once_with(self.course_run)
@mock.patch('course_discovery.apps.publisher.models.send_email_for_change_state')
def test_email_with_waffle_switch_disabled(self, send_email_for_change_state):
""" Verify that send_email_for_state not called with disable waffle switch.. """
toggle_switch('enable_publisher_email_notifications', False)
self.course_run.change_state(target=State.DRAFT)
send_email_for_change_state.assert_not_called()
def _assert_data(self):
""" DRY method to assert send email data"""
self.assertEqual([self.user.email, self.user_2.email], mail.outbox[0].to)
subject = 'Course Run {title}-{pacing_type}-{start} state has been changed.'.format(
title=self.course_run.course.title,
pacing_type=self.course_run.get_pacing_type_display(),
start=self.course_run.start.strftime("%B %d, %Y")
)
body = mail.outbox[0].body.strip()
self.assertEqual(
str(mail.outbox[0].subject),
subject
)
self.assertIn('Hi', body)
self.assertIn('The edX team', body)
'The following course run has been submitted for {{ state }}'.format(
state=self.course_run.state.name
)
page_path = reverse('publisher:publisher_course_run_detail', kwargs={'pk': self.course_run.id})
page_url = 'https://{host}{path}'.format(host=Site.objects.get_current().domain.strip('/'), path=page_path)
self.assertIn(page_url, body)
@ddt.data(
State.DRAFT, State.NEEDS_REVIEW, State.NEEDS_FINAL_APPROVAL,
State.FINALIZED, State.PUBLISHED, State.DRAFT
)
def test_email_without_group(self, target_state):
""" Verify that no email send if course group has no users. """
self.user.groups.remove(self.group)
self.user_2.groups.remove(self.group)
self.user_3.groups.remove(self.group)
self.course_run.change_state(target=target_state)
self.assertEqual(len(mail.outbox), 0)
@ddt.data(
State.DRAFT, State.NEEDS_REVIEW, State.NEEDS_FINAL_APPROVAL,
State.FINALIZED, State.PUBLISHED, State.DRAFT
)
def test_workflow_change_state_emails(self, target_state):
""" Verify that on each state change an email send to course group users. """
self.course_run.change_state(target=target_state)
self.assertEqual(len(mail.outbox), 1)
self._assert_data()
def test_email_without_start_date(self):
""" Verify that emails works properly even if course run does not have
start date.
"""
self.course_run.start = None
self.course_run.save()
self.course_run.change_state(target=State.DRAFT)
self.assertEqual(len(mail.outbox), 1)
# add the start date again for other tests.
self.course_run.start = datetime.datetime.now(pytz.UTC)
self.course_run.save()
......@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-10-25 21:30+0500\n"
"POT-Creation-Date: 2016-10-28 14:40+0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
......@@ -399,6 +399,11 @@ msgstr ""
msgid "JSON string containing an elasticsearch function score config."
msgstr ""
#: apps/publisher/emails.py
#, python-brace-format
msgid "Course Run {title}-{pacing_type}-{start} state has been changed."
msgstr ""
#: apps/publisher/forms.py
msgid "Yes"
msgstr ""
......@@ -1456,6 +1461,32 @@ msgstr ""
msgid "Looks like you haven't publish a course yet"
msgstr ""
#: templates/publisher/email/change_state.html
#: templates/publisher/email/change_state.txt
msgid "Hi,"
msgstr ""
#: templates/publisher/email/change_state.html
#: templates/publisher/email/change_state.txt
#, python-format
msgid "The following course run has been submitted for %(state_name)s."
msgstr ""
#: templates/publisher/email/change_state.html
msgid "View Course"
msgstr ""
#: templates/publisher/email/change_state.html
#: templates/publisher/email/change_state.txt
#: templates/publisher/email/comment.html
#: templates/publisher/email/comment.txt
msgid "The edX team"
msgstr ""
#: templates/publisher/email/change_state.txt
msgid "View Course:"
msgstr ""
#: templates/publisher/email/comment.html
#: templates/publisher/email/comment.txt
#, python-format
......@@ -1468,13 +1499,8 @@ msgstr ""
msgid "View comment"
msgstr ""
#: templates/publisher/email/comment.html
#: templates/publisher/email/comment.txt
msgid "The edX team"
msgstr ""
#: templates/publisher/email/comment.txt
msgid "View comment:"
msgid "View comment: "
msgstr ""
#: templates/publisher/seat_form.html
......
......@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-10-25 21:30+0500\n"
"POT-Creation-Date: 2016-10-28 14:40+0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
......
......@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-10-25 21:30+0500\n"
"POT-Creation-Date: 2016-10-28 14:40+0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
......@@ -494,6 +494,13 @@ msgstr ""
"JSÖN strïng çöntäïnïng än élästïçséärçh fünçtïön sçöré çönfïg. Ⱡ'σяєм ιρѕυм "
"∂σłσя ѕιт αмєт, ¢σηѕє¢тєтυя α#"
#: apps/publisher/emails.py
#, python-brace-format
msgid "Course Run {title}-{pacing_type}-{start} state has been changed."
msgstr ""
"Çöürsé Rün {title}-{pacing_type}-{start} stäté häs ßéén çhängéd. Ⱡ'σяєм "
"ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕє¢тєтυя α#"
#: apps/publisher/forms.py
msgid "Yes"
msgstr "Ýés Ⱡ'σяєм#"
......@@ -1696,6 +1703,34 @@ msgstr ""
"Lööks lïké ýöü hävén't püßlïsh ä çöürsé ýét Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, "
"¢σηѕє¢тєтυя #"
#: templates/publisher/email/change_state.html
#: templates/publisher/email/change_state.txt
msgid "Hi,"
msgstr "Hï, Ⱡ'σяєм#"
#: templates/publisher/email/change_state.html
#: templates/publisher/email/change_state.txt
#, python-format
msgid "The following course run has been submitted for %(state_name)s."
msgstr ""
"Thé föllöwïng çöürsé rün häs ßéén süßmïttéd för %(state_name)s. Ⱡ'σяєм ιρѕυм"
" ∂σłσя ѕιт αмєт, ¢σηѕє¢тєтυя α#"
#: templates/publisher/email/change_state.html
msgid "View Course"
msgstr "Vïéw Çöürsé Ⱡ'σяєм ιρѕυм ∂σłσя #"
#: templates/publisher/email/change_state.html
#: templates/publisher/email/change_state.txt
#: templates/publisher/email/comment.html
#: templates/publisher/email/comment.txt
msgid "The edX team"
msgstr "Thé édX téäm Ⱡ'σяєм ιρѕυм ∂σłσя ѕ#"
#: templates/publisher/email/change_state.txt
msgid "View Course:"
msgstr "Vïéw Çöürsé: Ⱡ'σяєм ιρѕυм ∂σłσя ѕ#"
#: templates/publisher/email/comment.html
#: templates/publisher/email/comment.txt
#, python-format
......@@ -1710,14 +1745,9 @@ msgstr ""
msgid "View comment"
msgstr "Vïéw çömmént Ⱡ'σяєм ιρѕυм ∂σłσя ѕ#"
#: templates/publisher/email/comment.html
#: templates/publisher/email/comment.txt
msgid "The edX team"
msgstr "Thé édX téäm Ⱡ'σяєм ιρѕυм ∂σłσя ѕ#"
#: templates/publisher/email/comment.txt
msgid "View comment:"
msgstr "Vïéw çömmént: Ⱡ'σяєм ιρѕυм ∂σłσя ѕι#"
msgid "View comment: "
msgstr "Vïéw çömmént: Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт#"
#: templates/publisher/seat_form.html
msgid "Seat Form"
......
......@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-10-25 21:30+0500\n"
"POT-Creation-Date: 2016-10-28 14:40+0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
......
{% extends "publisher/email/email_base.html" %}
{% load i18n %}
{% block body %}
<!-- Message Body -->
<p>{% trans "Hi," %}</p>
<p>
{% blocktrans trimmed %}
The following course run has been submitted for {{ state_name }}.
{% endblocktrans %}
</p>
<p>
<a href="{{ course_run_page_url }}">{% trans "View Course" %}</a>
</p>
<p>{% trans "The edX team" %}</p>
<!-- End Message Body -->
{% endblock body %}
{% load i18n %}
{% trans "Hi," %}
{% blocktrans trimmed %}
The following course run has been submitted for {{ state_name }}.
{% endblocktrans %}
{% trans "View Course:" %} {{ course_run_page_url }}
{% trans "The edX team" %}
......@@ -6,6 +6,6 @@
{{ comment.comment }}
{% trans "View comment:" %} {{ page_url }}
{% trans "View comment: " %}{{ page_url }}
{% trans "The edX team" %}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment