Commit e07a0101 by Awais Qureshi Committed by GitHub

Merge pull request #390 from edx/awais786/ECOM-6004-emails-for-course-states

Implement the emails for course run change states.
parents 75c3e54d 6a9a5128
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