Commit d38428ea by Tyler Hallada Committed by GitHub

Merge pull request #16129 from edx/thallada/refactor-verified-upgrade-reminder

Refactor recurring nudge and verified upgrade reminder emails
parents 3fcd7c69 d5b6b170
......@@ -30,4 +30,6 @@ class ScheduleAdmin(admin.ModelAdmin):
@admin.register(models.ScheduleConfig)
class ScheduleConfigAdmin(admin.ModelAdmin):
search_fields = ('site',)
list_display = ('site', 'create_schedules', 'enqueue_recurring_nudge', 'deliver_recurring_nudge')
list_display = ('site', 'create_schedules', 'enqueue_recurring_nudge',
'deliver_recurring_nudge', 'enqueue_upgrade_reminder',
'deliver_upgrade_reminder')
import datetime
import pytz
from django.contrib.sites.models import Site
from django.core.management.base import BaseCommand
from openedx.core.djangoapps.schedules.utils import PrefixedDebugLoggerMixin
class SendEmailBaseCommand(PrefixedDebugLoggerMixin, BaseCommand):
resolver_class = None # define in subclass
def add_arguments(self, parser):
parser.add_argument(
'--date',
default=datetime.datetime.utcnow().date().isoformat(),
help='The date to compute weekly messages relative to, in YYYY-MM-DD format',
)
parser.add_argument(
'--override-recipient-email',
help='Send all emails to this address instead of the actual recipient'
)
parser.add_argument('site_domain_name')
def handle(self, *args, **options):
resolver = self.make_resolver(*args, **options)
self.send_emails(resolver, *args, **options)
def make_resolver(self, *args, **options):
current_date = datetime.datetime(
*[int(x) for x in options['date'].split('-')],
tzinfo=pytz.UTC
)
self.log_debug('Args = %r', options)
self.log_debug('Current date = %s', current_date.isoformat())
site = Site.objects.get(domain__iexact=options['site_domain_name'])
self.log_debug('Running for site %s', site.domain)
return self.resolver_class(site, current_date)
def send_emails(self, resolver, *args, **options):
pass # define in subclass
from __future__ import print_function
from openedx.core.djangoapps.schedules.management.commands import SendEmailBaseCommand
from openedx.core.djangoapps.schedules.resolvers import ScheduleStartResolver
import datetime
import logging
from django.contrib.sites.models import Site
from django.core.management.base import BaseCommand
import pytz
class Command(SendEmailBaseCommand):
resolver_class = ScheduleStartResolver
from edx_ace.utils.date import serialize
from openedx.core.djangoapps.schedules.models import ScheduleConfig
from openedx.core.djangoapps.schedules.tasks import recurring_nudge_schedule_hour
from openedx.core.djangoapps.site_configuration.models import SiteConfiguration
def __init__(self, *args, **kwargs):
super(Command, self).__init__(*args, **kwargs)
self.log_prefix = 'Scheduled Nudge'
from edx_ace.recipient_resolver import RecipientResolver
LOG = logging.getLogger(__name__)
class ScheduleStartResolver(RecipientResolver):
def __init__(self, site, current_date):
self.site = site
self.current_date = current_date.replace(hour=0, minute=0, second=0)
def send(self, day, override_recipient_email=None):
"""
Send a message to all users whose schedule started at ``self.current_date`` - ``day``.
"""
if not ScheduleConfig.current(self.site).enqueue_recurring_nudge:
LOG.debug('Recurring Nudge: Message queuing disabled for site %s', self.site.domain)
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:
site_config = SiteConfiguration.objects.get(site_id=self.site.id)
org_list = site_config.values.get('course_org_filter', None)
exclude_orgs = False
if not org_list:
not_orgs = set()
for other_site_config in SiteConfiguration.objects.all():
not_orgs.update(other_site_config.values.get('course_org_filter', []))
org_list = list(not_orgs)
exclude_orgs = True
elif not isinstance(org_list, list):
org_list = [org_list]
except SiteConfiguration.DoesNotExist:
org_list = None
exclude_orgs = False
return exclude_orgs, org_list
class Command(BaseCommand):
def add_arguments(self, parser):
parser.add_argument(
'--date',
default=datetime.datetime.utcnow().date().isoformat(),
help='The date to compute weekly messages relative to, in YYYY-MM-DD format',
)
parser.add_argument(
'--override-recipient-email',
help='Send all emails to this address instead of the actual recipient'
)
parser.add_argument('site_domain_name')
def handle(self, *args, **options):
current_date = datetime.datetime(
*[int(x) for x in options['date'].split('-')],
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'])
LOG.debug('Scheduled Nudge: Running for site %s', site.domain)
resolver = ScheduleStartResolver(site, current_date)
for day in (3, 10):
resolver.send(day, options.get('override_recipient_email'))
def send_emails(self, resolver, *args, **options):
for day_offset in (-3, -10):
resolver.send(day_offset, options.get('override_recipient_email'))
from openedx.core.djangoapps.schedules.management.commands import SendEmailBaseCommand
from openedx.core.djangoapps.schedules.resolvers import UpgradeReminderResolver
class Command(SendEmailBaseCommand):
resolver_class = UpgradeReminderResolver
def __init__(self, *args, **kwargs):
super(Command, self).__init__(*args, **kwargs)
self.log_prefix = 'Upgrade Reminder'
def send_emails(self, resolver, *args, **options):
resolver.send(2, options.get('override_recipient_email'))
from __future__ import print_function
import datetime
from dateutil.tz import tzutc, gettz
from django.core.management.base import BaseCommand
from django.db.models import Prefetch
from django.conf import settings
from django.core.urlresolvers import reverse
from django.utils.http import urlquote
from openedx.core.djangoapps.schedules.message_type import ScheduleMessageType
from openedx.core.djangoapps.schedules.models import Schedule
from openedx.core.djangoapps.user_api.models import UserPreference
from edx_ace.recipient_resolver import RecipientResolver
from edx_ace import ace
from edx_ace.recipient import Recipient
from course_modes.models import CourseMode, format_course_price
from lms.djangoapps.experiments.utils import check_and_get_upgrade_link
class VerifiedUpgradeDeadlineReminder(ScheduleMessageType):
pass
class VerifiedDeadlineResolver(RecipientResolver):
def __init__(self, target_deadline):
self.target_deadline = target_deadline
def send(self, msg_type):
for (user, language, context) in self.build_email_context():
msg = msg_type.personalize(
Recipient(
user.username,
user.email,
),
language,
context
)
ace.send(msg)
def build_email_context(self):
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'
),
).filter(
upgrade_deadline__year=self.schedule_deadline.year,
upgrade_deadline__month=self.schedule_deadline.month,
upgrade_deadline__day=self.schedule_deadline.day,
)
if "read_replica" in settings.DATABASES:
schedules = schedules.using("read_replica")
for schedule in schedules:
enrollment = schedule.enrollment
user = enrollment.user
user_time_zone = tzutc()
for preference in user.tzprefs:
user_time_zone = gettz(preference.value)
course_id_str = str(enrollment.course_id)
course = enrollment.course
course_root = reverse('course_root', kwargs={'course_id': urlquote(course_id_str)})
def absolute_url(relative_path):
return u'{}{}'.format(settings.LMS_ROOT_URL, relative_path)
template_context = {
'user_full_name': user.profile.name,
'user_personal_address': user.profile.name if user.profile.name else user.username,
'user_username': user.username,
'user_time_zone': user_time_zone,
'user_schedule_start_time': schedule.start,
'user_schedule_verified_upgrade_deadline_time': schedule.upgrade_deadline,
'course_id': course_id_str,
'course_title': course.display_name,
'course_url': absolute_url(course_root),
'course_image_url': absolute_url(course.course_image_url),
'course_end_time': course.end,
'course_verified_upgrade_url': check_and_get_upgrade_link(course, user),
'course_verified_upgrade_price': format_course_price(course.verified_modes[0].min_price),
}
yield (user, course.language, template_context)
class Command(BaseCommand):
def add_arguments(self, parser):
parser.add_argument('--date', default=datetime.datetime.utcnow().date().isoformat())
def handle(self, *args, **options):
current_date = datetime.date(*[int(x) for x in options['date'].split('-')])
msg_t = VerifiedUpgradeDeadlineReminder()
for offset in (2, 9, 16):
target_date = current_date + datetime.timedelta(days=offset)
VerifiedDeadlineResolver(target_date).send(msg_t)
import datetime
from unittest import skipUnless
import ddt
import pytz
from django.conf import settings
from mock import patch
from openedx.core.djangoapps.schedules.management.commands import SendEmailBaseCommand
from openedx.core.djangoapps.site_configuration.tests.factories import SiteFactory
from openedx.core.djangolib.testing.utils import CacheIsolationTestCase, skip_unless_lms
@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 TestSendEmailBaseCommand(CacheIsolationTestCase):
def setUp(self):
self.command = SendEmailBaseCommand()
def test_init_resolver_class(self):
assert self.command.resolver_class is None
def test_make_resolver(self):
with patch.object(self.command, 'resolver_class') as resolver_class:
example_site = SiteFactory(domain='example.com')
self.command.make_resolver(site_domain_name='example.com', date='2017-09-29')
resolver_class.assert_called_once_with(
example_site,
datetime.datetime(2017, 9, 29, tzinfo=pytz.UTC)
)
def test_handle(self):
with patch.object(self.command, 'make_resolver') as make_resolver:
make_resolver.return_value = 'resolver'
with patch.object(self.command, 'send_emails') as send_emails:
self.command.handle(date='2017-09-29')
make_resolver.assert_called_once_with(date='2017-09-29')
send_emails.assert_called_once_with('resolver', date='2017-09-29')
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('schedules', '0003_scheduleconfig'),
]
operations = [
migrations.AddField(
model_name='scheduleconfig',
name='deliver_upgrade_reminder',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='scheduleconfig',
name='enqueue_upgrade_reminder',
field=models.BooleanField(default=False),
),
]
......@@ -35,3 +35,5 @@ class ScheduleConfig(ConfigurationModel):
create_schedules = models.BooleanField(default=False)
enqueue_recurring_nudge = models.BooleanField(default=False)
deliver_recurring_nudge = models.BooleanField(default=False)
enqueue_upgrade_reminder = models.BooleanField(default=False)
deliver_upgrade_reminder = models.BooleanField(default=False)
import datetime
from edx_ace.recipient_resolver import RecipientResolver
from edx_ace.utils.date import serialize
from openedx.core.djangoapps.schedules.models import ScheduleConfig
from openedx.core.djangoapps.schedules.tasks import (
DEFAULT_NUM_BINS,
RECURRING_NUDGE_NUM_BINS,
UPGRADE_REMINDER_NUM_BINS,
recurring_nudge_schedule_bin,
upgrade_reminder_schedule_bin
)
from openedx.core.djangoapps.schedules.utils import PrefixedDebugLoggerMixin
from openedx.core.djangoapps.site_configuration.models import SiteConfiguration
class BinnedSchedulesBaseResolver(PrefixedDebugLoggerMixin, RecipientResolver):
"""
Starts num_bins number of async tasks, each of which sends emails to an equal group of learners.
Arguments:
site -- Site object that filtered Schedules will be a part of
current_date -- datetime that will be used (with time zeroed-out) as the current date in the queries
Static attributes:
async_send_task -- celery task function which this resolver will call out to
num_bins -- the int number of bins to split the users into
enqueue_config_var -- the string field name of the config variable on ScheduleConfig to check before enqueuing
"""
async_send_task = None # define in subclass
num_bins = DEFAULT_NUM_BINS
enqueue_config_var = None # define in subclass
def __init__(self, site, current_date, *args, **kwargs):
super(BinnedSchedulesBaseResolver, self).__init__(*args, **kwargs)
self.site = site
self.current_date = current_date.replace(hour=0, minute=0, second=0)
def send(self, day_offset, override_recipient_email=None):
if not self.is_enqueue_enabled():
self.log_debug('Message queuing disabled for site %s', self.site.domain)
return
exclude_orgs, org_list = self.get_course_org_filter()
target_date = self.current_date + datetime.timedelta(days=day_offset)
self.log_debug('Target date = %s', target_date.isoformat())
for bin in range(self.num_bins):
task_args = (
self.site.id, serialize(target_date), day_offset, bin, org_list, exclude_orgs, override_recipient_email,
)
self.log_debug('Launching task with args = %r', task_args)
self.async_send_task.apply_async(
task_args,
retry=False,
)
def is_enqueue_enabled(self):
if self.enqueue_config_var:
return getattr(ScheduleConfig.current(self.site), self.enqueue_config_var)
return False
def get_course_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:
site_config = SiteConfiguration.objects.get(site_id=self.site.id)
org_list = site_config.get_value('course_org_filter')
exclude_orgs = False
if not org_list:
not_orgs = set()
for other_site_config in SiteConfiguration.objects.all():
other = other_site_config.get_value('course_org_filter')
if not isinstance(other, list):
if other is not None:
not_orgs.add(other)
else:
not_orgs.update(other)
org_list = list(not_orgs)
exclude_orgs = True
elif not isinstance(org_list, list):
org_list = [org_list]
except SiteConfiguration.DoesNotExist:
org_list = None
exclude_orgs = False
finally:
return exclude_orgs, org_list
class ScheduleStartResolver(BinnedSchedulesBaseResolver):
"""
Send a message to all users whose schedule started at ``self.current_date`` + ``day_offset``.
"""
async_send_task = recurring_nudge_schedule_bin
num_bins = RECURRING_NUDGE_NUM_BINS
enqueue_config_var = 'enqueue_recurring_nudge'
def __init__(self, *args, **kwargs):
super(ScheduleStartResolver, self).__init__(*args, **kwargs)
self.log_prefix = 'Scheduled Nudge'
class UpgradeReminderResolver(BinnedSchedulesBaseResolver):
"""
Send a message to all users whose verified upgrade deadline is at ``self.current_date`` + ``day_offset``.
"""
async_send_task = upgrade_reminder_schedule_bin
num_bins = UPGRADE_REMINDER_NUM_BINS
enqueue_config_var = 'enqueue_upgrade_reminder'
def __init__(self, *args, **kwargs):
super(UpgradeReminderResolver, self).__init__(*args, **kwargs)
self.log_prefix = 'Upgrade Reminder'
from urlparse import urlparse
from django.conf import settings
from django.core.urlresolvers import reverse
from django.utils.http import urlquote
from edxmako.shortcuts import marketing_link
def get_base_template_context(site):
"""Dict with entries needed for all templates that use the base template"""
return {
# Platform information
'homepage_url': encode_url(marketing_link('ROOT')),
'dashboard_url': absolute_url(site, reverse('dashboard')),
'template_revision': settings.EDX_PLATFORM_REVISION,
'platform_name': settings.PLATFORM_NAME,
'contact_mailing_address': settings.CONTACT_MAILING_ADDRESS,
'social_media_urls': encode_urls_in_dict(getattr(settings, 'SOCIAL_MEDIA_FOOTER_URLS', {})),
'mobile_store_urls': encode_urls_in_dict(getattr(settings, 'MOBILE_STORE_URLS', {})),
}
def encode_url(url):
# Sailthru has a bug where URLs that contain "+" characters in their path components are misinterpreted
# when GA instrumentation is enabled. We need to percent-encode the path segments of all URLs that are
# injected into our templates to work around this issue.
parsed_url = urlparse(url)
modified_url = parsed_url._replace(path=urlquote(parsed_url.path))
return modified_url.geturl()
def absolute_url(site, relative_path):
root = site.domain.rstrip('/')
relative_path = relative_path.lstrip('/')
return encode_url(u'https://{root}/{path}'.format(root=root, path=relative_path))
def encode_urls_in_dict(mapping):
urls = {}
for key, value in mapping.iteritems():
urls[key] = encode_url(value)
return urls
{% extends 'schedules/edx_ace/common/base_body.html' %}
{% load i18n %}
{% load static %}
{% block preview_text %}
{% blocktrans trimmed %}
We hope you are enjoying learning with us so far in {{ course_name }}! A verified certificate
will allow you to highlight your new knowledge and skills. It's official, and easily shareable.
Upgrade by {{ user_schedule_upgrade_deadline_time }}.
{% endblocktrans %}
{% endblock %}
{% block content %}
<table width="100%" align="left" border="0" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td>
<h1>{% trans "Upgrade now" %}</h1>
<p>
{% blocktrans trimmed %}
We hope you are enjoying learning with us so far in <strong>{{ course_name }}</strong>! A
verified certificate will allow you to highlight your new knowledge and skills. It's official,
and easily shareable.
{% endblocktrans %}
</p>
<p>
{% blocktrans trimmed %}
Upgrade by <strong>{{ user_schedule_upgrade_deadline_time }}</strong>.
{% endblocktrans %}
</p>
<a href="{{ course_url }}">
<img
src="{{ cert_image }}"
alt="{% blocktrans %}Example print-out of a verified certificate{% endblocktrans %}"
style="
display: block;
margin-right: auto;
margin-left: auto;
margin-top: 50px;
margin-bottom: 50px;
border-top: 1px solid lightgray;
border-bottom: 3px solid lightgray;
border-right: 3px solid lightgray;
border-left: 1px solid lightgray;
" />
</a>
<p>
<!-- email client support for style sheets is pretty spotty, so we have to inline all of these styles -->
<a
href="{{ course_url }}"
style="
color: #ffffff;
text-decoration: none;
border-radius: 4px;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
background-color: #005686;
border-top: 10px solid #005686;
border-bottom: 10px solid #005686;
border-right: 16px solid #005686;
border-left: 16px solid #005686;
display: inline-block;
">
<!-- old email clients require the use of the font tag :( -->
<font color="#ffffff"><b>{% trans "Upgrade now" %}</b></font>
</a>
</p>
</td>
</tr>
</table>
{% endblock %}
{% load i18n %}
{% blocktrans trimmed %}
Dear {{ user_personal_address }},
{% endblocktrans %}
{% blocktrans trimmed %}
We hope you are enjoying learning with us so far in {{ course_name }}! A verified certificate
will allow you to highlight your new knowledge and skills. It's official, and easily shareable.
Upgrade by {{ user_schedule_upgrade_deadline_time }}.
{% endblocktrans %}
{% trans "Upgrade now at" %} <{{ course_url }}>
{% load i18n %}
{% blocktrans %}Upgrade to earn a verified certificate in {{ course_name }}{% endblocktrans %}
Dear {{ user_personal_address }},
<br/>
We hope you are enjoying {{ course_title }}.
Upgrade by {{ user_schedule_verified_upgrade_deadline_time|date:"l, F dS, Y" }}
to get a shareable certificate!
<br/>
<a href="{{course_verified_upgrade_url}}">Upgrade now</a>
Dear {{ user_personal_address }},
We hope you are enjoying {{ course_title }}.
Upgrade by {{ user_schedule_verified_upgrade_deadline_time|date:"l, F dS, Y" }} to get a shareable certificate!
Upgrade now at {{course_verified_upgrade_url}}
......@@ -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
import datetime
from unittest import skipUnless
import ddt
from django.conf import settings
from mock import patch
from openedx.core.djangoapps.schedules.resolvers import BinnedSchedulesBaseResolver
from openedx.core.djangoapps.schedules.tasks import DEFAULT_NUM_BINS
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
@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 TestBinnedSchedulesBaseResolver(CacheIsolationTestCase):
def setUp(self):
super(TestBinnedSchedulesBaseResolver, self).setUp()
self.site = SiteFactory.create()
self.site_config = SiteConfigurationFactory.create(site=self.site)
self.schedule_config = ScheduleConfigFactory.create(site=self.site)
def setup_resolver(self, site=None, current_date=None):
if site is None:
site = self.site
if current_date is None:
current_date = datetime.datetime.now()
resolver = BinnedSchedulesBaseResolver(self.site, current_date)
return resolver
def test_init_site(self):
resolver = self.setup_resolver()
assert resolver.site == self.site
def test_init_current_date(self):
current_time = datetime.datetime.now()
resolver = self.setup_resolver(current_date=current_time)
current_date = current_time.replace(hour=0, minute=0, second=0)
assert resolver.current_date == current_date
def test_init_async_send_task(self):
resolver = self.setup_resolver()
assert resolver.async_send_task is None
def test_init_num_bins(self):
resolver = self.setup_resolver()
assert resolver.num_bins == DEFAULT_NUM_BINS
def test_send_enqueue_disabled(self):
resolver = self.setup_resolver()
resolver.is_enqueue_enabled = lambda: False
with patch.object(resolver, 'async_send_task') as send:
with patch.object(resolver, 'log_debug') as log_debug:
resolver.send(day_offset=2)
log_debug.assert_called_once_with('Message queuing disabled for site %s', self.site.domain)
send.apply_async.assert_not_called()
@ddt.data(0, 2, -3)
def test_send_enqueue_enabled(self, day_offset):
resolver = self.setup_resolver()
resolver.is_enqueue_enabled = lambda: True
resolver.get_course_org_filter = lambda: (False, None)
with patch.object(resolver, 'async_send_task') as send:
with patch.object(resolver, 'log_debug') as log_debug:
resolver.send(day_offset=day_offset)
target_date = resolver.current_date + datetime.timedelta(day_offset)
log_debug.assert_any_call('Target date = %s', target_date.isoformat())
assert send.apply_async.call_count == DEFAULT_NUM_BINS
@ddt.data(True, False)
def test_is_enqueue_enabled(self, enabled):
resolver = self.setup_resolver()
resolver.enqueue_config_var = 'enqueue_recurring_nudge'
self.schedule_config.enqueue_recurring_nudge = enabled
self.schedule_config.save()
assert resolver.is_enqueue_enabled() == enabled
@ddt.unpack
@ddt.data(
('course1', ['course1']),
(['course1', 'course2'], ['course1', 'course2'])
)
def test_get_course_org_filter_include(self, course_org_filter, expected_org_list):
resolver = self.setup_resolver()
self.site_config.values['course_org_filter'] = course_org_filter
self.site_config.save()
exclude_orgs, org_list = resolver.get_course_org_filter()
assert not exclude_orgs
assert org_list == expected_org_list
@ddt.unpack
@ddt.data(
(None, []),
('course1', [u'course1']),
(['course1', 'course2'], [u'course1', u'course2'])
)
def test_get_course_org_filter_exclude(self, course_org_filter, expected_org_list):
resolver = self.setup_resolver()
self.other_site = SiteFactory.create()
self.other_site_config = SiteConfigurationFactory.create(
site=self.other_site,
values={'course_org_filter': course_org_filter},
)
exclude_orgs, org_list = resolver.get_course_org_filter()
assert exclude_orgs
self.assertItemsEqual(org_list, expected_org_list)
import logging
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):
super(PrefixedDebugLoggerMixin, self).__init__(*args, **kwargs)
self.log_prefix = self.__class__.__name__
def log_debug(self, message, *args, **kwargs):
LOG.debug(self.log_prefix + ': ' + message, *args, **kwargs)
......@@ -3,7 +3,7 @@ Model factories for unit testing views or models.
"""
from django.contrib.sites.models import Site
from factory.django import DjangoModelFactory
from factory import SubFactory, Sequence, SelfAttribute
from factory import SubFactory, Sequence, SelfAttribute, lazy_attribute
from openedx.core.djangoapps.site_configuration.models import SiteConfiguration
......@@ -27,6 +27,9 @@ class SiteConfigurationFactory(DjangoModelFactory):
class Meta(object):
model = SiteConfiguration
values = {}
enabled = True
site = SubFactory(SiteFactory)
@lazy_attribute
def values(self):
return {}
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