Commit fab1769b by Gabe Mulley Committed by Gabe Mulley

update nudge templates

parent de0380ca
...@@ -14,7 +14,6 @@ from openedx.core.djangoapps.site_configuration.models import SiteConfiguration ...@@ -14,7 +14,6 @@ from openedx.core.djangoapps.site_configuration.models import SiteConfiguration
from edx_ace.recipient_resolver import RecipientResolver from edx_ace.recipient_resolver import RecipientResolver
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
...@@ -28,8 +27,28 @@ class ScheduleStartResolver(RecipientResolver): ...@@ -28,8 +27,28 @@ class ScheduleStartResolver(RecipientResolver):
Send a message to all users whose schedule started at ``self.current_date`` - ``day``. Send a message to all users whose schedule started at ``self.current_date`` - ``day``.
""" """
if not ScheduleConfig.current(self.site).enqueue_recurring_nudge: if not ScheduleConfig.current(self.site).enqueue_recurring_nudge:
LOG.debug('Recurring Nudge: Message queuing disabled for site %s', self.site.domain)
return return
exclude_orgs, org_list = self.get_org_filter()
target_date = self.current_date - datetime.timedelta(days=day)
LOG.debug('Scheduled Nudge: Target date = %s', target_date.isoformat())
for hour in range(24):
target_hour = target_date + datetime.timedelta(hours=hour)
task_args = (self.site.id, day, serialize(target_hour), org_list, exclude_orgs, override_recipient_email)
LOG.debug('Scheduled Nudge: Launching task with args = %r', task_args)
recurring_nudge_schedule_hour.apply_async(task_args, retry=False)
def get_org_filter(self):
"""
Given the configuration of sites, get the list of orgs that should be included or excluded from this send.
Returns:
tuple: Returns a tuple (exclude_orgs, org_list). If exclude_orgs is True, then org_list is a list of the
only orgs that should be included in this send. If exclude_orgs is False, then org_list is a list of
orgs that should be excluded from this send. All other orgs should be included.
"""
try: try:
site_config = SiteConfiguration.objects.get(site_id=self.site.id) site_config = SiteConfiguration.objects.get(site_id=self.site.id)
org_list = site_config.values.get('course_org_filter', None) org_list = site_config.values.get('course_org_filter', None)
...@@ -45,14 +64,7 @@ class ScheduleStartResolver(RecipientResolver): ...@@ -45,14 +64,7 @@ class ScheduleStartResolver(RecipientResolver):
except SiteConfiguration.DoesNotExist: except SiteConfiguration.DoesNotExist:
org_list = None org_list = None
exclude_orgs = False exclude_orgs = False
return exclude_orgs, org_list
target_date = self.current_date - datetime.timedelta(days=day)
for hour in range(24):
target_hour = target_date + datetime.timedelta(hours=hour)
recurring_nudge_schedule_hour.apply_async(
(self.site.id, day, serialize(target_hour), org_list, exclude_orgs, override_recipient_email),
retry=False,
)
class Command(BaseCommand): class Command(BaseCommand):
...@@ -74,7 +86,11 @@ class Command(BaseCommand): ...@@ -74,7 +86,11 @@ class Command(BaseCommand):
*[int(x) for x in options['date'].split('-')], *[int(x) for x in options['date'].split('-')],
tzinfo=pytz.UTC tzinfo=pytz.UTC
) )
LOG.debug('Scheduled Nudge: Args = %r', options)
LOG.debug('Scheduled Nudge: Current date = %s', current_date.isoformat())
site = Site.objects.get(domain__iexact=options['site_domain_name']) site = Site.objects.get(domain__iexact=options['site_domain_name'])
LOG.debug('Scheduled Nudge: Running for site %s', site.domain)
resolver = ScheduleStartResolver(site, current_date) resolver = ScheduleStartResolver(site, current_date)
for day in (3, 10): for day in (3, 10):
resolver.send(day, options.get('override_recipient_email')) resolver.send(day, options.get('override_recipient_email'))
...@@ -143,20 +143,30 @@ class TestSendRecurringNudge(CacheIsolationTestCase): ...@@ -143,20 +143,30 @@ class TestSendRecurringNudge(CacheIsolationTestCase):
for config in (limited_config, unlimited_config): for config in (limited_config, unlimited_config):
ScheduleConfigFactory.create(site=config.site) ScheduleConfigFactory.create(site=config.site)
user1 = UserFactory.create()
user2 = UserFactory.create()
ScheduleFactory.create( ScheduleFactory.create(
start=datetime.datetime(2017, 8, 2, 17, 44, 30, tzinfo=pytz.UTC), start=datetime.datetime(2017, 8, 2, 17, 44, 30, tzinfo=pytz.UTC),
enrollment__course__org=filtered_org, enrollment__course__org=filtered_org,
enrollment__user=user1,
)
ScheduleFactory.create(
start=datetime.datetime(2017, 8, 2, 17, 44, 30, tzinfo=pytz.UTC),
enrollment__course__org=unfiltered_org,
enrollment__user=user1,
)
ScheduleFactory.create(
start=datetime.datetime(2017, 8, 2, 17, 44, 30, tzinfo=pytz.UTC),
enrollment__course__org=unfiltered_org,
enrollment__user=user2,
) )
for _ in range(2):
ScheduleFactory.create(
start=datetime.datetime(2017, 8, 2, 17, 44, 30, tzinfo=pytz.UTC),
enrollment__course__org=unfiltered_org,
)
test_time_str = serialize(datetime.datetime(2017, 8, 2, 17, tzinfo=pytz.UTC)) test_time_str = serialize(datetime.datetime(2017, 8, 2, 17, tzinfo=pytz.UTC))
with self.assertNumQueries(2): with self.assertNumQueries(2):
tasks.recurring_nudge_schedule_hour( tasks.recurring_nudge_schedule_hour(
limited_config.site.id, 3, test_time_str, org_list=org_list, exclude_orgs=exclude_orgs, limited_config.site.id, day=3, target_hour_str=test_time_str, org_list=org_list,
exclude_orgs=exclude_orgs,
) )
self.assertEqual(mock_schedule_send.apply_async.call_count, expected_message_count) self.assertEqual(mock_schedule_send.apply_async.call_count, expected_message_count)
......
import datetime import datetime
from itertools import groupby from itertools import groupby
from logging import getLogger import logging
from urlparse import urlparse from urlparse import urlparse
from celery.task import task from celery.task import task
...@@ -12,6 +12,7 @@ from django.core.urlresolvers import reverse ...@@ -12,6 +12,7 @@ from django.core.urlresolvers import reverse
from django.db.models import Min from django.db.models import Min
from django.db.utils import DatabaseError from django.db.utils import DatabaseError
from django.utils.http import urlquote from django.utils.http import urlquote
from edx_ace import ace from edx_ace import ace
from edx_ace.message import Message from edx_ace.message import Message
from edx_ace.recipient import Recipient from edx_ace.recipient import Recipient
...@@ -22,7 +23,8 @@ from edxmako.shortcuts import marketing_link ...@@ -22,7 +23,8 @@ from edxmako.shortcuts import marketing_link
from openedx.core.djangoapps.schedules.message_type import ScheduleMessageType from openedx.core.djangoapps.schedules.message_type import ScheduleMessageType
from openedx.core.djangoapps.schedules.models import Schedule, ScheduleConfig from openedx.core.djangoapps.schedules.models import Schedule, ScheduleConfig
log = getLogger(__name__)
LOG = logging.getLogger(__name__)
ROUTING_KEY = getattr(settings, 'ACE_ROUTING_KEY', None) ROUTING_KEY = getattr(settings, 'ACE_ROUTING_KEY', None)
...@@ -45,7 +47,7 @@ def update_course_schedules(self, **kwargs): ...@@ -45,7 +47,7 @@ def update_course_schedules(self, **kwargs):
) )
except Exception as exc: # pylint: disable=broad-except except Exception as exc: # pylint: disable=broad-except
if not isinstance(exc, KNOWN_RETRY_ERRORS): if not isinstance(exc, KNOWN_RETRY_ERRORS):
log.exception("Unexpected failure: task id: %s, kwargs=%s".format(self.request.id, kwargs)) LOG.exception("Unexpected failure: task id: %s, kwargs=%s".format(self.request.id, kwargs))
raise self.retry(kwargs=kwargs, exc=exc) raise self.retry(kwargs=kwargs, exc=exc)
...@@ -78,9 +80,11 @@ def recurring_nudge_schedule_hour( ...@@ -78,9 +80,11 @@ def recurring_nudge_schedule_hour(
def _recurring_nudge_schedule_send(site_id, msg_str): def _recurring_nudge_schedule_send(site_id, msg_str):
site = Site.objects.get(pk=site_id) site = Site.objects.get(pk=site_id)
if not ScheduleConfig.current(site).deliver_recurring_nudge: if not ScheduleConfig.current(site).deliver_recurring_nudge:
LOG.debug('Recurring Nudge: Message delivery disabled for site %s', site.domain)
return return
msg = Message.from_string(msg_str) msg = Message.from_string(msg_str)
LOG.debug('Recurring Nudge: Sending message = %s', msg_str)
ace.send(msg) ace.send(msg)
...@@ -97,12 +101,6 @@ def _recurring_nudge_schedules_for_hour(target_hour, org_list, exclude_orgs=Fals ...@@ -97,12 +101,6 @@ def _recurring_nudge_schedules_for_hour(target_hour, org_list, exclude_orgs=Fals
first_schedule__lt=target_hour + datetime.timedelta(minutes=60) first_schedule__lt=target_hour + datetime.timedelta(minutes=60)
) )
if org_list is not None:
if exclude_orgs:
users = users.exclude(courseenrollment__course__org__in=org_list)
else:
users = users.filter(courseenrollment__course__org__in=org_list)
schedules = Schedule.objects.select_related( schedules = Schedule.objects.select_related(
'enrollment__user__profile', 'enrollment__user__profile',
'enrollment__course', 'enrollment__course',
...@@ -122,6 +120,8 @@ def _recurring_nudge_schedules_for_hour(target_hour, org_list, exclude_orgs=Fals ...@@ -122,6 +120,8 @@ def _recurring_nudge_schedules_for_hour(target_hour, org_list, exclude_orgs=Fals
if "read_replica" in settings.DATABASES: if "read_replica" in settings.DATABASES:
schedules = schedules.using("read_replica") schedules = schedules.using("read_replica")
LOG.debug('Scheduled Nudge: Query = %r', schedules.query.sql_with_params())
dashboard_relative_url = reverse('dashboard') dashboard_relative_url = reverse('dashboard')
for (user, user_schedules) in groupby(schedules, lambda s: s.enrollment.user): for (user, user_schedules) in groupby(schedules, lambda s: s.enrollment.user):
......
...@@ -15,13 +15,8 @@ email itself. --> ...@@ -15,13 +15,8 @@ email itself. -->
{% block preview_text %}{% endblock %} {% block preview_text %}{% endblock %}
</div> </div>
{# This "View on Web" link must appear in the document before any layout tables so that accessible email clients can #} {# Note {beacon_src} is not a template variable that is evaluated by the Django template engine. It is evaluated by #}
{# send the user to a proper web browser which can parse the email correctly. #}
{# Note {view_url} is not a template variable that is evaluated by the Django template engine. It is evaluated by #}
{# Sailthru when the email is sent. Other email providers would need to replace this variable in the HTML as well. #} {# Sailthru when the email is sent. Other email providers would need to replace this variable in the HTML as well. #}
<p><a href="{view_url}">{% trans "View on Web" %}</a></p>
{# Note this is another late-bound variable #}
<img src="{beacon_src}" alt="" role="presentation" aria-hidden="true" /> <img src="{beacon_src}" alt="" role="presentation" aria-hidden="true" />
{% get_current_language as LANGUAGE_CODE %} {% get_current_language as LANGUAGE_CODE %}
...@@ -156,10 +151,17 @@ email itself. --> ...@@ -156,10 +151,17 @@ email itself. -->
<tr> <tr>
<!-- Actions --> <!-- Actions -->
<td style="padding-bottom: 20px;"> <td style="padding-bottom: 20px;">
{# Note that this variable is evaluated by Sailthru, not the Django template engine #} {# Note that these variables are evaluated by Sailthru, not the Django template engine #}
<a href="{optout_confirm_url}" style="color: #005686"> <p>
<font color="#005686"><b>{% trans "Unsubscribe from this list" %}</b></font> <a href="{view_url}" style="color: #005686">
</a> <font color="#005686"><b>{% trans "View on Web" %}</b></font>
</a>
</p>
<p>
<a href="{optout_confirm_url}" style="color: #005686">
<font color="#005686"><b>{% trans "Unsubscribe from this list" %}</b></font>
</a>
</p>
</td> </td>
</tr> </tr>
<tr> <tr>
......
{% load i18n %} {% load i18n %}
{% if courses|length > 1 %} {% if courses|length > 1 %}
{% blocktrans %}Keep learning on {{ platform_name }}!{% endblocktrans %} {% blocktrans %}Keep learning on {{ platform_name }}{% endblocktrans %}
{% else %} {% else %}
{% blocktrans %}Keep learning in {{course_name}} !{% endblocktrans %} {% blocktrans %}Keep learning in {{course_name}}{% endblocktrans %}
{% endif %} {% endif %}
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