Add tests for upgrade_reminder
Fix test_templates By passing the date pre-formatted to ace. Address PR comments Fix linting Add TODO comment about using LoggerAdapter More lint fixes Fix upgrade_reminder task from silently failing
This commit is contained in:
@@ -15,6 +15,8 @@ from openedx.core.djangoapps.site_configuration.models import SiteConfiguration
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# TODO: consider using a LoggerAdapter instead of this mixin:
|
||||
# https://docs.python.org/2/library/logging.html#logging.LoggerAdapter
|
||||
class PrefixedDebugLoggerMixin(object):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.log_prefix = self.__class__.__name__
|
||||
|
||||
@@ -11,7 +11,7 @@ from openedx.core.djangoapps.schedules.management.commands import (
|
||||
SendEmailBaseCommand,
|
||||
BinnedSchedulesBaseResolver
|
||||
)
|
||||
from openedx.core.djangoapps.schedules.tests.factories import ScheduleConfigFactory, ScheduleFactory
|
||||
from openedx.core.djangoapps.schedules.tests.factories import ScheduleConfigFactory
|
||||
from openedx.core.djangoapps.site_configuration.tests.factories import SiteConfigurationFactory, SiteFactory
|
||||
from openedx.core.djangolib.testing.utils import CacheIsolationTestCase, skip_unless_lms
|
||||
|
||||
@@ -96,7 +96,6 @@ class TestBinnedSchedulesBaseResolver(CacheIsolationTestCase):
|
||||
assert not exclude_orgs
|
||||
assert org_list == expected_org_list
|
||||
|
||||
# factory_boy doesn't make sense at all
|
||||
@ddt.unpack
|
||||
@ddt.data(
|
||||
(None, []),
|
||||
|
||||
@@ -61,7 +61,7 @@ class TestSendRecurringNudge(CacheIsolationTestCase):
|
||||
retry=False,
|
||||
)
|
||||
mock_schedule_bin.apply_async.assert_any_call(
|
||||
(self.site_config.site.id, serialize(test_time), -3, 23, [], True, None),
|
||||
(self.site_config.site.id, serialize(test_time), -3, tasks.RECURRING_NUDGE_NUM_BINS - 1, [], True, None),
|
||||
retry=False,
|
||||
)
|
||||
self.assertFalse(mock_ace.send.called)
|
||||
|
||||
@@ -0,0 +1,257 @@
|
||||
import datetime
|
||||
import itertools
|
||||
from copy import deepcopy
|
||||
from unittest import skipUnless
|
||||
|
||||
import attr
|
||||
import ddt
|
||||
import pytz
|
||||
from django.conf import settings
|
||||
from edx_ace.channel import ChannelType
|
||||
from edx_ace.test_utils import StubPolicy, patch_channels, patch_policies
|
||||
from edx_ace.utils.date import serialize
|
||||
from mock import Mock, patch
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
from opaque_keys.edx.locator import CourseLocator
|
||||
|
||||
from openedx.core.djangoapps.schedules import tasks
|
||||
from openedx.core.djangoapps.schedules.management.commands import send_upgrade_reminder as reminder
|
||||
from openedx.core.djangoapps.schedules.tests.factories import ScheduleConfigFactory, ScheduleFactory
|
||||
from openedx.core.djangoapps.site_configuration.tests.factories import SiteConfigurationFactory, SiteFactory
|
||||
from openedx.core.djangolib.testing.utils import CacheIsolationTestCase, skip_unless_lms
|
||||
from student.tests.factories import UserFactory
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
@skip_unless_lms
|
||||
@skipUnless('openedx.core.djangoapps.schedules.apps.SchedulesConfig' in settings.INSTALLED_APPS,
|
||||
"Can't test schedules if the app isn't installed")
|
||||
class TestUpgradeReminder(CacheIsolationTestCase):
|
||||
# pylint: disable=protected-access
|
||||
|
||||
def setUp(self):
|
||||
super(TestUpgradeReminder, self).setUp()
|
||||
|
||||
ScheduleFactory.create(upgrade_deadline=datetime.datetime(2017, 8, 1, 15, 44, 30, tzinfo=pytz.UTC))
|
||||
ScheduleFactory.create(upgrade_deadline=datetime.datetime(2017, 8, 1, 17, 34, 30, tzinfo=pytz.UTC))
|
||||
ScheduleFactory.create(upgrade_deadline=datetime.datetime(2017, 8, 2, 15, 34, 30, tzinfo=pytz.UTC))
|
||||
|
||||
site = SiteFactory.create()
|
||||
self.site_config = SiteConfigurationFactory.create(site=site)
|
||||
ScheduleConfigFactory.create(site=self.site_config.site)
|
||||
|
||||
@patch.object(reminder, 'UpgradeReminderResolver')
|
||||
def test_handle(self, mock_resolver):
|
||||
test_time = datetime.datetime(2017, 8, 1, tzinfo=pytz.UTC)
|
||||
reminder.Command().handle(date='2017-08-01', site_domain_name=self.site_config.site.domain)
|
||||
mock_resolver.assert_called_with(self.site_config.site, test_time)
|
||||
|
||||
mock_resolver().send.assert_any_call(2, None)
|
||||
|
||||
@patch.object(tasks, 'ace')
|
||||
@patch.object(reminder, 'upgrade_reminder_schedule_bin')
|
||||
def test_resolver_send(self, mock_schedule_bin, mock_ace):
|
||||
current_time = datetime.datetime(2017, 8, 1, tzinfo=pytz.UTC)
|
||||
test_time = current_time + datetime.timedelta(days=2)
|
||||
ScheduleFactory.create(upgrade_deadline=datetime.datetime(2017, 8, 3, 15, 34, 30, tzinfo=pytz.UTC))
|
||||
|
||||
reminder.UpgradeReminderResolver(self.site_config.site, current_time).send(2)
|
||||
self.assertFalse(mock_schedule_bin.called)
|
||||
mock_schedule_bin.apply_async.assert_any_call(
|
||||
(self.site_config.site.id, serialize(test_time), 2, 0, [], True, None),
|
||||
retry=False,
|
||||
)
|
||||
mock_schedule_bin.apply_async.assert_any_call(
|
||||
(self.site_config.site.id, serialize(test_time), 2, tasks.UPGRADE_REMINDER_NUM_BINS - 1, [], True, None),
|
||||
retry=False,
|
||||
)
|
||||
self.assertFalse(mock_ace.send.called)
|
||||
|
||||
@ddt.data(1, 10, 100)
|
||||
@patch.object(tasks, 'ace')
|
||||
@patch.object(tasks, '_upgrade_reminder_schedule_send')
|
||||
def test_schedule_bin(self, schedule_count, mock_schedule_send, mock_ace):
|
||||
schedules = [
|
||||
ScheduleFactory.create(
|
||||
upgrade_deadline=datetime.datetime(2017, 8, 3, 18, 44, 30, tzinfo=pytz.UTC),
|
||||
enrollment__user=UserFactory.create(),
|
||||
enrollment__course__id=CourseLocator('edX', 'toy', 'Bin')
|
||||
) for _ in range(schedule_count)
|
||||
]
|
||||
|
||||
test_time = datetime.datetime(2017, 8, 3, 18, tzinfo=pytz.UTC)
|
||||
test_time_str = serialize(test_time)
|
||||
with self.assertNumQueries(25):
|
||||
for b in range(tasks.UPGRADE_REMINDER_NUM_BINS):
|
||||
tasks.upgrade_reminder_schedule_bin(
|
||||
self.site_config.site.id, target_day_str=test_time_str, day_offset=2, bin_num=b,
|
||||
org_list=[schedules[0].enrollment.course.org],
|
||||
)
|
||||
self.assertEqual(mock_schedule_send.apply_async.call_count, schedule_count)
|
||||
self.assertFalse(mock_ace.send.called)
|
||||
|
||||
@patch.object(tasks, '_upgrade_reminder_schedule_send')
|
||||
def test_no_course_overview(self, mock_schedule_send):
|
||||
|
||||
schedule = ScheduleFactory.create(
|
||||
upgrade_deadline=datetime.datetime(2017, 8, 3, 20, 34, 30, tzinfo=pytz.UTC),
|
||||
)
|
||||
schedule.enrollment.course_id = CourseKey.from_string('edX/toy/Not_2012_Fall')
|
||||
schedule.enrollment.save()
|
||||
|
||||
test_time = datetime.datetime(2017, 8, 3, 20, tzinfo=pytz.UTC)
|
||||
test_time_str = serialize(test_time)
|
||||
with self.assertNumQueries(25):
|
||||
for b in range(tasks.UPGRADE_REMINDER_NUM_BINS):
|
||||
tasks.upgrade_reminder_schedule_bin(
|
||||
self.site_config.site.id, target_day_str=test_time_str, day_offset=2, bin_num=b,
|
||||
org_list=[schedule.enrollment.course.org],
|
||||
)
|
||||
|
||||
# There is no database constraint that enforces that enrollment.course_id points
|
||||
# to a valid CourseOverview object. However, in that case, schedules isn't going
|
||||
# to attempt to address it, and will instead simply skip those users.
|
||||
# This happens 'transparently' because django generates an inner-join between
|
||||
# enrollment and course_overview, and thus will skip any rows where course_overview
|
||||
# is null.
|
||||
self.assertEqual(mock_schedule_send.apply_async.call_count, 0)
|
||||
|
||||
@patch.object(tasks, 'ace')
|
||||
def test_delivery_disabled(self, mock_ace):
|
||||
ScheduleConfigFactory.create(site=self.site_config.site, deliver_upgrade_reminder=False)
|
||||
|
||||
mock_msg = Mock()
|
||||
tasks._upgrade_reminder_schedule_send(self.site_config.site.id, mock_msg)
|
||||
self.assertFalse(mock_ace.send.called)
|
||||
|
||||
@patch.object(tasks, 'ace')
|
||||
@patch.object(reminder, 'upgrade_reminder_schedule_bin')
|
||||
def test_enqueue_disabled(self, mock_schedule_bin, mock_ace):
|
||||
ScheduleConfigFactory.create(site=self.site_config.site, enqueue_upgrade_reminder=False)
|
||||
|
||||
current_time = datetime.datetime(2017, 8, 1, tzinfo=pytz.UTC)
|
||||
reminder.UpgradeReminderResolver(self.site_config.site, current_time).send(3)
|
||||
self.assertFalse(mock_schedule_bin.called)
|
||||
self.assertFalse(mock_schedule_bin.apply_async.called)
|
||||
self.assertFalse(mock_ace.send.called)
|
||||
|
||||
@patch.object(tasks, 'ace')
|
||||
@patch.object(tasks, '_upgrade_reminder_schedule_send')
|
||||
@ddt.data(
|
||||
((['filtered_org'], False, 1)),
|
||||
((['filtered_org'], True, 2))
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_site_config(self, org_list, exclude_orgs, expected_message_count, mock_schedule_send, mock_ace):
|
||||
filtered_org = 'filtered_org'
|
||||
unfiltered_org = 'unfiltered_org'
|
||||
site1 = SiteFactory.create(domain='foo1.bar', name='foo1.bar')
|
||||
limited_config = SiteConfigurationFactory.create(values={'course_org_filter': [filtered_org]}, site=site1)
|
||||
site2 = SiteFactory.create(domain='foo2.bar', name='foo2.bar')
|
||||
unlimited_config = SiteConfigurationFactory.create(values={'course_org_filter': []}, site=site2)
|
||||
|
||||
for config in (limited_config, unlimited_config):
|
||||
ScheduleConfigFactory.create(site=config.site)
|
||||
|
||||
user1 = UserFactory.create(id=tasks.UPGRADE_REMINDER_NUM_BINS)
|
||||
user2 = UserFactory.create(id=tasks.UPGRADE_REMINDER_NUM_BINS * 2)
|
||||
|
||||
ScheduleFactory.create(
|
||||
upgrade_deadline=datetime.datetime(2017, 8, 3, 17, 44, 30, tzinfo=pytz.UTC),
|
||||
enrollment__course__org=filtered_org,
|
||||
enrollment__user=user1,
|
||||
)
|
||||
ScheduleFactory.create(
|
||||
upgrade_deadline=datetime.datetime(2017, 8, 3, 17, 44, 30, tzinfo=pytz.UTC),
|
||||
enrollment__course__org=unfiltered_org,
|
||||
enrollment__user=user1,
|
||||
)
|
||||
ScheduleFactory.create(
|
||||
upgrade_deadline=datetime.datetime(2017, 8, 3, 17, 44, 30, tzinfo=pytz.UTC),
|
||||
enrollment__course__org=unfiltered_org,
|
||||
enrollment__user=user2,
|
||||
)
|
||||
|
||||
test_time = datetime.datetime(2017, 8, 3, 17, tzinfo=pytz.UTC)
|
||||
test_time_str = serialize(test_time)
|
||||
with self.assertNumQueries(2):
|
||||
tasks.upgrade_reminder_schedule_bin(
|
||||
limited_config.site.id, target_day_str=test_time_str, day_offset=2, bin_num=0,
|
||||
org_list=org_list, exclude_orgs=exclude_orgs,
|
||||
)
|
||||
|
||||
self.assertEqual(mock_schedule_send.apply_async.call_count, expected_message_count)
|
||||
self.assertFalse(mock_ace.send.called)
|
||||
|
||||
@patch.object(tasks, 'ace')
|
||||
@patch.object(tasks, '_upgrade_reminder_schedule_send')
|
||||
def test_multiple_enrollments(self, mock_schedule_send, mock_ace):
|
||||
user = UserFactory.create()
|
||||
schedules = [
|
||||
ScheduleFactory.create(
|
||||
upgrade_deadline=datetime.datetime(2017, 8, 3, 19, 44, 30, tzinfo=pytz.UTC),
|
||||
enrollment__user=user,
|
||||
enrollment__course__id=CourseLocator('edX', 'toy', 'Course{}'.format(course_num))
|
||||
)
|
||||
for course_num in (1, 2, 3)
|
||||
]
|
||||
|
||||
test_time = datetime.datetime(2017, 8, 3, 19, 44, 30, tzinfo=pytz.UTC)
|
||||
test_time_str = serialize(test_time)
|
||||
with self.assertNumQueries(2):
|
||||
tasks.upgrade_reminder_schedule_bin(
|
||||
self.site_config.site.id, target_day_str=test_time_str, day_offset=2,
|
||||
bin_num=user.id % tasks.UPGRADE_REMINDER_NUM_BINS,
|
||||
org_list=[schedules[0].enrollment.course.org],
|
||||
)
|
||||
self.assertEqual(mock_schedule_send.apply_async.call_count, 3)
|
||||
self.assertFalse(mock_ace.send.called)
|
||||
|
||||
@ddt.data(*itertools.product((1, 10, 100), (2, 10)))
|
||||
@ddt.unpack
|
||||
def test_templates(self, message_count, day):
|
||||
|
||||
user = UserFactory.create()
|
||||
schedules = [
|
||||
ScheduleFactory.create(
|
||||
upgrade_deadline=datetime.datetime(2017, 8, 3, 19, 44, 30, tzinfo=pytz.UTC),
|
||||
enrollment__user=user,
|
||||
enrollment__course__id=CourseLocator('edX', 'toy', 'Course{}'.format(course_num))
|
||||
)
|
||||
for course_num in range(message_count)
|
||||
]
|
||||
|
||||
test_time = datetime.datetime(2017, 8, 3, 19, tzinfo=pytz.UTC)
|
||||
test_time_str = serialize(test_time)
|
||||
|
||||
patch_policies(self, [StubPolicy([ChannelType.PUSH])])
|
||||
mock_channel = Mock(
|
||||
name='test_channel',
|
||||
channel_type=ChannelType.EMAIL
|
||||
)
|
||||
patch_channels(self, [mock_channel])
|
||||
|
||||
sent_messages = []
|
||||
|
||||
templates_override = deepcopy(settings.TEMPLATES)
|
||||
templates_override[0]['OPTIONS']['string_if_invalid'] = "TEMPLATE WARNING - MISSING VARIABLE [%s]"
|
||||
with self.settings(TEMPLATES=templates_override):
|
||||
with patch.object(tasks, '_upgrade_reminder_schedule_send') as mock_schedule_send:
|
||||
mock_schedule_send.apply_async = lambda args, *_a, **_kw: sent_messages.append(args)
|
||||
|
||||
with self.assertNumQueries(2):
|
||||
tasks.upgrade_reminder_schedule_bin(
|
||||
self.site_config.site.id, target_day_str=test_time_str, day_offset=day,
|
||||
bin_num=user.id % tasks.UPGRADE_REMINDER_NUM_BINS,
|
||||
org_list=[schedules[0].enrollment.course.org],
|
||||
)
|
||||
|
||||
self.assertEqual(len(sent_messages), message_count)
|
||||
|
||||
for args in sent_messages:
|
||||
tasks._upgrade_reminder_schedule_send(*args)
|
||||
|
||||
self.assertEqual(mock_channel.deliver.call_count, message_count)
|
||||
for (_name, (_msg, email), _kwargs) in mock_channel.deliver.mock_calls:
|
||||
for template in attr.astuple(email):
|
||||
self.assertNotIn("TEMPLATE WARNING", template)
|
||||
@@ -8,8 +8,9 @@ from django.contrib.auth.models import User
|
||||
from django.contrib.sites.models import Site
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.db.models import F, Min, Prefetch
|
||||
from django.db.models import F, Min
|
||||
from django.db.utils import DatabaseError
|
||||
from django.utils.formats import dateformat, get_format
|
||||
|
||||
from edx_ace import ace
|
||||
from edx_ace.message import Message
|
||||
@@ -17,7 +18,6 @@ from edx_ace.recipient import Recipient
|
||||
from edx_ace.utils.date import deserialize
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
|
||||
from course_modes.models import CourseMode
|
||||
from edxmako.shortcuts import marketing_link
|
||||
from openedx.core.djangoapps.schedules.message_type import ScheduleMessageType
|
||||
from openedx.core.djangoapps.schedules.models import Schedule, ScheduleConfig
|
||||
@@ -27,7 +27,6 @@ from openedx.core.djangoapps.schedules.template_context import (
|
||||
encode_urls_in_dict,
|
||||
get_base_template_context
|
||||
)
|
||||
from openedx.core.djangoapps.user_api.models import UserPreference
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
@@ -232,9 +231,7 @@ def _recurring_nudge_schedules_for_bin(target_day, bin_num, org_list, exclude_or
|
||||
|
||||
|
||||
class UpgradeReminder(ScheduleMessageType):
|
||||
def __init__(self, day, *args, **kwargs):
|
||||
super(UpgradeReminder, self).__init__(*args, **kwargs)
|
||||
self.name = "upgradereminder".format(day)
|
||||
pass
|
||||
|
||||
|
||||
@task(ignore_result=True, routing_key=ROUTING_KEY)
|
||||
@@ -242,7 +239,7 @@ def upgrade_reminder_schedule_bin(
|
||||
site_id, target_day_str, day_offset, bin_num, org_list, exclude_orgs=False, override_recipient_email=None,
|
||||
):
|
||||
target_day = deserialize(target_day_str)
|
||||
msg_type = UpgradeReminder(abs(day_offset))
|
||||
msg_type = UpgradeReminder()
|
||||
|
||||
for (user, language, context) in _upgrade_reminder_schedules_for_bin(target_day, bin_num, org_list, exclude_orgs):
|
||||
msg = msg_type.personalize(
|
||||
@@ -267,29 +264,34 @@ def _upgrade_reminder_schedule_send(site_id, msg_str):
|
||||
|
||||
|
||||
def _upgrade_reminder_schedules_for_bin(target_day, bin_num, org_list, exclude_orgs=False):
|
||||
beginning_of_day = target_day.replace(hour=0, minute=0, second=0)
|
||||
users = User.objects.filter(
|
||||
courseenrollment__schedule__upgrade_deadline__gte=beginning_of_day,
|
||||
courseenrollment__schedule__upgrade_deadline__lt=beginning_of_day + datetime.timedelta(days=1),
|
||||
courseenrollment__is_active=True,
|
||||
).annotate(
|
||||
first_schedule=Min('courseenrollment__schedule__upgrade_deadline')
|
||||
).annotate(
|
||||
id_mod=F('id') % UPGRADE_REMINDER_NUM_BINS
|
||||
).filter(
|
||||
id_mod=bin_num
|
||||
)
|
||||
|
||||
schedules = Schedule.objects.select_related(
|
||||
'enrollment__user__profile',
|
||||
'enrollment__course',
|
||||
).prefetch_related(
|
||||
Prefetch(
|
||||
'enrollment__course__modes',
|
||||
queryset=CourseMode.objects.filter(mode_slug=CourseMode.VERIFIED),
|
||||
to_attr='verified_modes'
|
||||
),
|
||||
Prefetch(
|
||||
'enrollment__user__preferences',
|
||||
queryset=UserPreference.objects.filter(key='time_zone'),
|
||||
to_attr='tzprefs'
|
||||
),
|
||||
).annotate(
|
||||
id_mod=F('enrollment__user__id') % UPGRADE_REMINDER_NUM_BINS
|
||||
).filter(
|
||||
id_mod=bin_num
|
||||
).filter(
|
||||
upgrade_deadline__year=target_day.year,
|
||||
upgrade_deadline__month=target_day.month,
|
||||
upgrade_deadline__day=target_day.day,
|
||||
)
|
||||
enrollment__user__in=users,
|
||||
upgrade_deadline__gte=beginning_of_day,
|
||||
upgrade_deadline__lt=beginning_of_day + datetime.timedelta(days=1),
|
||||
enrollment__is_active=True,
|
||||
).order_by('enrollment__user__id')
|
||||
|
||||
if org_list is not None:
|
||||
if exclude_orgs:
|
||||
schedules = schedules.exclude(enrollment__course__org__in=org_list)
|
||||
else:
|
||||
schedules = schedules.filter(enrollment__course__org__in=org_list)
|
||||
|
||||
if "read_replica" in settings.DATABASES:
|
||||
schedules = schedules.using("read_replica")
|
||||
@@ -299,7 +301,6 @@ def _upgrade_reminder_schedules_for_bin(target_day, bin_num, org_list, exclude_o
|
||||
user = enrollment.user
|
||||
|
||||
course_id_str = str(enrollment.course_id)
|
||||
course = enrollment.course
|
||||
|
||||
# TODO: group by schedule and user like recurring nudge
|
||||
course_id_strs = [course_id_str]
|
||||
@@ -309,7 +310,14 @@ def _upgrade_reminder_schedules_for_bin(target_day, bin_num, org_list, exclude_o
|
||||
template_context.update({
|
||||
'student_name': user.profile.name,
|
||||
'user_personal_address': user.profile.name if user.profile.name else user.username,
|
||||
'user_schedule_upgrade_deadline_time': schedule.upgrade_deadline,
|
||||
'user_schedule_upgrade_deadline_time': dateformat.format(
|
||||
schedule.upgrade_deadline,
|
||||
get_format(
|
||||
'DATE_FORMAT',
|
||||
lang=first_schedule.enrollment.course.language,
|
||||
use_l10n=True
|
||||
)
|
||||
),
|
||||
|
||||
'course_name': first_schedule.enrollment.course.display_name,
|
||||
'course_url': absolute_url(reverse('course_root', args=[str(first_schedule.enrollment.course_id)])),
|
||||
@@ -318,4 +326,4 @@ def _upgrade_reminder_schedules_for_bin(target_day, bin_num, org_list, exclude_o
|
||||
'course_ids': course_id_strs,
|
||||
})
|
||||
|
||||
yield (user, course.language, template_context)
|
||||
yield (user, first_schedule.enrollment.course.language, template_context)
|
||||
|
||||
@@ -2,15 +2,15 @@
|
||||
{% load i18n %}
|
||||
|
||||
{% block preview_text %}
|
||||
{% if courses|length > 1 %}
|
||||
{% if course_ids|length > 1 %}
|
||||
{% blocktrans trimmed %}
|
||||
We hope you are enjoying {{ course_name }}, and other courses on edX.org.
|
||||
Upgrade by {{ user_schedule_upgrade_deadline_time|date:"l, F dS, Y" }} to get a shareable certificate!
|
||||
Upgrade by {{ user_schedule_upgrade_deadline_time }} to get a shareable certificate!
|
||||
{% endblocktrans %}
|
||||
{% else %}
|
||||
{% blocktrans trimmed %}
|
||||
We hope you are enjoying {{ course_name }}.
|
||||
Upgrade by {{ user_schedule_upgrade_deadline_time|date:"l, F dS, Y" }} to get a shareable certificate!
|
||||
Upgrade by {{ user_schedule_upgrade_deadline_time }} to get a shareable certificate!
|
||||
{% endblocktrans %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
@@ -22,16 +22,15 @@
|
||||
<h1>{% trans "Upgrade now" %}</h1>
|
||||
|
||||
<p>
|
||||
{% if courses|length > 1 %}
|
||||
{% if course_ids|length > 1 %}
|
||||
{% blocktrans trimmed %}
|
||||
We hope you are enjoying <strong>{{ course_name }}</strong>, and other courses on edX.org.
|
||||
{{ user_schedule_upgrade_deadline_time|date }}
|
||||
Upgrade by {{ user_schedule_upgrade_deadline_time|date:"l, F dS, Y" }} to get a shareable certificate!
|
||||
Upgrade by {{ user_schedule_upgrade_deadline_time }} to get a shareable certificate!
|
||||
{% endblocktrans %}
|
||||
{% else %}
|
||||
{% blocktrans trimmed %}
|
||||
We hope you are enjoying <strong>{{ course_name }}</strong>.
|
||||
Upgrade by {{ user_schedule_upgrade_deadline_time|date:"l, F dS, Y" }} to get a shareable certificate!
|
||||
Upgrade by {{ user_schedule_upgrade_deadline_time }} to get a shareable certificate!
|
||||
{% endblocktrans %}
|
||||
{% endif %}
|
||||
</p>
|
||||
@@ -39,7 +38,7 @@
|
||||
<p>
|
||||
<!-- email client support for style sheets is pretty spotty, so we have to inline all of these styles -->
|
||||
<a
|
||||
{% if courses|length > 1 %}
|
||||
{% if course_ids|length > 1 %}
|
||||
href="{{ dashboard_url }}"
|
||||
{% else %}
|
||||
href="{{ course_url }}"
|
||||
|
||||
@@ -4,17 +4,17 @@
|
||||
Dear {{ user_personal_address }},
|
||||
{% endblocktrans %}
|
||||
|
||||
{% if courses|length > 1 %}
|
||||
{% if course_ids|length > 1 %}
|
||||
{% blocktrans trimmed %}
|
||||
We hope you are enjoying {{ course_name }}, and other courses on edX.org.
|
||||
Upgrade by {{ user_schedule_upgrade_deadline_time|date:"l, F dS, Y" }} to get a shareable certificate!
|
||||
Upgrade by {{ user_schedule_upgrade_deadline_time }} to get a shareable certificate!
|
||||
{% endblocktrans %}
|
||||
|
||||
{% trans "Upgrade now at" %} <{{ dashboard_url }}>
|
||||
{% else %}
|
||||
{% blocktrans trimmed %}
|
||||
We hope you are enjoying {{ course_name }}.
|
||||
Upgrade by {{ user_schedule_upgrade_deadline_time|date:"l, F dS, Y" }} to get a shareable certificate!
|
||||
Upgrade by {{ user_schedule_upgrade_deadline_time }} to get a shareable certificate!
|
||||
{% endblocktrans %}
|
||||
|
||||
{% trans "Upgrade now at" %} <{{ course_url }}>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{% if courses|length > 1 %}
|
||||
{% if course_ids|length > 1 %}
|
||||
{{ platform_name }}
|
||||
{% else %}
|
||||
{{ course_name }}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{% load i18n %}
|
||||
|
||||
{% if courses|length > 1 %}
|
||||
{% if course_ids|length > 1 %}
|
||||
{% blocktrans %}Only two days left to upgrade on {{ platform_name }}!{% endblocktrans %}
|
||||
{% else %}
|
||||
{% blocktrans %}Only two days left to upgrade in {{course_name}} !{% endblocktrans %}
|
||||
|
||||
@@ -23,3 +23,5 @@ class ScheduleConfigFactory(factory.DjangoModelFactory):
|
||||
create_schedules = True
|
||||
enqueue_recurring_nudge = True
|
||||
deliver_recurring_nudge = True
|
||||
enqueue_upgrade_reminder = True
|
||||
deliver_upgrade_reminder = True
|
||||
|
||||
Reference in New Issue
Block a user