diff --git a/lms/djangoapps/certificates/tests/test_webview_views.py b/lms/djangoapps/certificates/tests/test_webview_views.py index f14f907b67..b87934b7ed 100644 --- a/lms/djangoapps/certificates/tests/test_webview_views.py +++ b/lms/djangoapps/certificates/tests/test_webview_views.py @@ -807,7 +807,7 @@ class CertificatesViewsTests(CommonCertificatesTestCase): as the issued date for instructor-paced courses """ self.course.self_paced = self_paced - today = datetime.datetime.today() + today = datetime.datetime.utcnow() self.course.certificate_available_date = today + datetime.timedelta(cert_avail_delta) self.store.update_item(self.course, self.user.id) self._add_course_certificates(count=1, signatory_count=1, is_active=True) diff --git a/lms/djangoapps/courseware/tests/test_date_summary.py b/lms/djangoapps/courseware/tests/test_date_summary.py index b9ccee892d..069edb6f2b 100644 --- a/lms/djangoapps/courseware/tests/test_date_summary.py +++ b/lms/djangoapps/courseware/tests/test_date_summary.py @@ -26,7 +26,7 @@ from courseware.models import DynamicUpgradeDeadlineConfiguration, CourseDynamic from lms.djangoapps.verify_student.models import VerificationDeadline from lms.djangoapps.verify_student.tests.factories import SoftwareSecurePhotoVerificationFactory from openedx.core.djangoapps.content.course_overviews.models import CourseOverview -from openedx.core.djangoapps.schedules.signals import SCHEDULE_WAFFLE_FLAG +from openedx.core.djangoapps.schedules.signals import CREATE_SCHEDULE_WAFFLE_FLAG from openedx.core.djangoapps.self_paced.models import SelfPacedConfiguration from openedx.core.djangoapps.site_configuration.tests.factories import SiteFactory from openedx.core.djangoapps.user_api.preferences.api import set_user_preference @@ -481,7 +481,7 @@ class TestScheduleOverrides(SharedModuleStoreTestCase): mock_get_current_site.return_value = SiteFactory.create() - @override_waffle_flag(SCHEDULE_WAFFLE_FLAG, True) + @override_waffle_flag(CREATE_SCHEDULE_WAFFLE_FLAG, True) def test_date_with_self_paced_with_enrollment_before_course_start(self): """ Enrolling before a course begins should result in the upgrade deadline being set relative to the course start date. """ @@ -493,7 +493,7 @@ class TestScheduleOverrides(SharedModuleStoreTestCase): block = VerifiedUpgradeDeadlineDate(course, enrollment.user) self.assertEqual(block.date, expected) - @override_waffle_flag(SCHEDULE_WAFFLE_FLAG, True) + @override_waffle_flag(CREATE_SCHEDULE_WAFFLE_FLAG, True) def test_date_with_self_paced_with_enrollment_after_course_start(self): """ Enrolling after a course begins should result in the upgrade deadline being set relative to the enrollment date. """ @@ -513,7 +513,7 @@ class TestScheduleOverrides(SharedModuleStoreTestCase): expected = enrollment.created + timedelta(days=course_config.deadline_days) self.assertEqual(block.date, expected) - @override_waffle_flag(SCHEDULE_WAFFLE_FLAG, True) + @override_waffle_flag(CREATE_SCHEDULE_WAFFLE_FLAG, True) def test_date_with_self_paced_without_dynamic_upgrade_deadline(self): """ Disabling the dynamic upgrade deadline functionality should result in the verified mode's expiration date being returned. """ @@ -524,7 +524,7 @@ class TestScheduleOverrides(SharedModuleStoreTestCase): block = VerifiedUpgradeDeadlineDate(course, enrollment.user) self.assertEqual(block.date, expected) - @override_waffle_flag(SCHEDULE_WAFFLE_FLAG, True) + @override_waffle_flag(CREATE_SCHEDULE_WAFFLE_FLAG, True) def test_date_with_self_paced_with_single_course(self): """ If the global switch is off, a single course can still be enabled. """ course = create_self_paced_course_run(days_till_start=-1) @@ -536,7 +536,7 @@ class TestScheduleOverrides(SharedModuleStoreTestCase): expected = enrollment.created + timedelta(days=course_config.deadline_days) self.assertEqual(block.date, expected) - @override_waffle_flag(SCHEDULE_WAFFLE_FLAG, True) + @override_waffle_flag(CREATE_SCHEDULE_WAFFLE_FLAG, True) def test_date_with_existing_schedule(self): """ If a schedule is created while deadlines are disabled, they shouldn't magically appear once the feature is turned on. """ diff --git a/openedx/core/djangoapps/schedules/config.py b/openedx/core/djangoapps/schedules/config.py new file mode 100644 index 0000000000..d072c41519 --- /dev/null +++ b/openedx/core/djangoapps/schedules/config.py @@ -0,0 +1,12 @@ +from openedx.core.djangoapps.waffle_utils import WaffleFlagNamespace, CourseWaffleFlag, WaffleFlag + + +WAFFLE_FLAG_NAMESPACE = WaffleFlagNamespace(name=u'schedules') + +CREATE_SCHEDULE_WAFFLE_FLAG = CourseWaffleFlag( + waffle_namespace=WAFFLE_FLAG_NAMESPACE, + flag_name=u'create_schedules_for_course', + flag_undefined_default=False +) + +DEBUG_MESSAGE_WAFFLE_FLAG = WaffleFlag(WAFFLE_FLAG_NAMESPACE, u'enable_debugging') diff --git a/openedx/core/djangoapps/schedules/management/commands/send_verified_upgrade_deadline_reminder.py b/openedx/core/djangoapps/schedules/management/commands/send_verified_upgrade_deadline_reminder.py index a0d39ab2b2..a78fe79526 100644 --- a/openedx/core/djangoapps/schedules/management/commands/send_verified_upgrade_deadline_reminder.py +++ b/openedx/core/djangoapps/schedules/management/commands/send_verified_upgrade_deadline_reminder.py @@ -4,17 +4,15 @@ import datetime from dateutil.tz import tzutc, gettz from django.core.management.base import BaseCommand -from django.test.utils import CaptureQueriesContext from django.db.models import Prefetch from django.conf import settings from django.core.urlresolvers import reverse -from django.db import DEFAULT_DB_ALIAS, connections 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.message import MessageType from edx_ace.recipient_resolver import RecipientResolver from edx_ace import ace from edx_ace.recipient import Recipient @@ -24,7 +22,7 @@ from course_modes.models import CourseMode, format_course_price from lms.djangoapps.experiments.utils import check_and_get_upgrade_link -class VerifiedUpgradeDeadlineReminder(MessageType): +class VerifiedUpgradeDeadlineReminder(ScheduleMessageType): pass diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py index 30b5aff256..a72662c7cd 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py @@ -30,6 +30,8 @@ class TestSendRecurringNudge(CacheIsolationTestCase): # pylint: disable=protected-access def setUp(self): + super(TestSendRecurringNudge, self).setUp() + ScheduleFactory.create(start=datetime.datetime(2017, 8, 1, 15, 44, 30, tzinfo=pytz.UTC)) ScheduleFactory.create(start=datetime.datetime(2017, 8, 1, 17, 34, 30, tzinfo=pytz.UTC)) ScheduleFactory.create(start=datetime.datetime(2017, 8, 2, 15, 34, 30, tzinfo=pytz.UTC)) @@ -74,7 +76,7 @@ class TestSendRecurringNudge(CacheIsolationTestCase): ] test_time_str = serialize(datetime.datetime(2017, 8, 1, 18, tzinfo=pytz.UTC)) - with self.assertNumQueries(1): + with self.assertNumQueries(2): tasks.recurring_nudge_schedule_hour( self.site_config.site.id, 3, test_time_str, [schedules[0].enrollment.course.org], ) @@ -91,7 +93,7 @@ class TestSendRecurringNudge(CacheIsolationTestCase): schedule.enrollment.save() test_time_str = serialize(datetime.datetime(2017, 8, 1, 20, tzinfo=pytz.UTC)) - with self.assertNumQueries(1): + with self.assertNumQueries(2): tasks.recurring_nudge_schedule_hour( self.site_config.site.id, 3, test_time_str, [schedule.enrollment.course.org], ) @@ -152,7 +154,7 @@ class TestSendRecurringNudge(CacheIsolationTestCase): ) test_time_str = serialize(datetime.datetime(2017, 8, 2, 17, tzinfo=pytz.UTC)) - with self.assertNumQueries(1): + with self.assertNumQueries(2): tasks.recurring_nudge_schedule_hour( limited_config.site.id, 3, test_time_str, org_list=org_list, exclude_orgs=exclude_orgs, ) @@ -180,7 +182,7 @@ class TestSendRecurringNudge(CacheIsolationTestCase): ] test_time_str = serialize(datetime.datetime(2017, 8, 1, test_hour, tzinfo=pytz.UTC)) - with self.assertNumQueries(1): + with self.assertNumQueries(2): tasks.recurring_nudge_schedule_hour( self.site_config.site.id, 3, test_time_str, [schedules[0].enrollment.course.org], ) @@ -217,7 +219,7 @@ class TestSendRecurringNudge(CacheIsolationTestCase): with patch.object(tasks, '_recurring_nudge_schedule_send') as mock_schedule_send: mock_schedule_send.apply_async = lambda args, *_a, **_kw: sent_messages.append(args) - with self.assertNumQueries(1): + with self.assertNumQueries(2): tasks.recurring_nudge_schedule_hour( self.site_config.site.id, day, test_time_str, [schedules[0].enrollment.course.org], ) diff --git a/openedx/core/djangoapps/schedules/message_type.py b/openedx/core/djangoapps/schedules/message_type.py new file mode 100644 index 0000000000..fc3574232b --- /dev/null +++ b/openedx/core/djangoapps/schedules/message_type.py @@ -0,0 +1,11 @@ +import logging + +from edx_ace.message import MessageType + +from openedx.core.djangoapps.schedules.config import DEBUG_MESSAGE_WAFFLE_FLAG + + +class ScheduleMessageType(MessageType): + def __init__(self, *args, **kwargs): + super(ScheduleMessageType, self).__init__(*args, **kwargs) + self.log_level = logging.DEBUG if DEBUG_MESSAGE_WAFFLE_FLAG.is_enabled() else None diff --git a/openedx/core/djangoapps/schedules/signals.py b/openedx/core/djangoapps/schedules/signals.py index 2a8f01c44f..cbf5201fd4 100644 --- a/openedx/core/djangoapps/schedules/signals.py +++ b/openedx/core/djangoapps/schedules/signals.py @@ -10,8 +10,8 @@ from courseware.models import DynamicUpgradeDeadlineConfiguration, CourseDynamic from edx_ace.utils import date from openedx.core.djangoapps.signals.signals import COURSE_START_DATE_CHANGED from openedx.core.djangoapps.theming.helpers import get_current_site -from openedx.core.djangoapps.waffle_utils import WaffleFlagNamespace, CourseWaffleFlag from student.models import CourseEnrollment +from .config import CREATE_SCHEDULE_WAFFLE_FLAG from .models import Schedule, ScheduleConfig from .tasks import update_course_schedules @@ -19,13 +19,6 @@ from .tasks import update_course_schedules log = logging.getLogger(__name__) -SCHEDULE_WAFFLE_FLAG = CourseWaffleFlag( - waffle_namespace=WaffleFlagNamespace('schedules'), - flag_name='create_schedules_for_course', - flag_undefined_default=False -) - - @receiver(post_save, sender=CourseEnrollment, dispatch_uid='create_schedule_for_enrollment') def create_schedule(sender, **kwargs): if not kwargs['created']: @@ -41,7 +34,7 @@ def create_schedule(sender, **kwargs): schedule_config = ScheduleConfig.current(current_site) if ( not schedule_config.create_schedules - and not SCHEDULE_WAFFLE_FLAG.is_enabled(enrollment.course_id) + and not CREATE_SCHEDULE_WAFFLE_FLAG.is_enabled(enrollment.course_id) ): log.debug('Schedules: Creation not enabled for this course or for this site') return diff --git a/openedx/core/djangoapps/schedules/tasks.py b/openedx/core/djangoapps/schedules/tasks.py index 166e86a30f..01df3bfd48 100644 --- a/openedx/core/djangoapps/schedules/tasks.py +++ b/openedx/core/djangoapps/schedules/tasks.py @@ -13,12 +13,13 @@ from django.db.models import Min from django.db.utils import DatabaseError from django.utils.http import urlquote from edx_ace import ace -from edx_ace.message import Message, MessageType +from edx_ace.message import Message from edx_ace.recipient import Recipient from edx_ace.utils.date import deserialize from opaque_keys.edx.keys import CourseKey from edxmako.shortcuts import marketing_link +from openedx.core.djangoapps.schedules.message_type import ScheduleMessageType from openedx.core.djangoapps.schedules.models import Schedule, ScheduleConfig log = getLogger(__name__) @@ -48,7 +49,7 @@ def update_course_schedules(self, **kwargs): raise self.retry(kwargs=kwargs, exc=exc) -class RecurringNudge(MessageType): +class RecurringNudge(ScheduleMessageType): def __init__(self, day, *args, **kwargs): super(RecurringNudge, self).__init__(*args, **kwargs) self.name = "recurringnudge_day{}".format(day) diff --git a/openedx/core/djangoapps/schedules/tests/test_signals.py b/openedx/core/djangoapps/schedules/tests/test_signals.py index 3d5c550b11..1bf0048c5e 100644 --- a/openedx/core/djangoapps/schedules/tests/test_signals.py +++ b/openedx/core/djangoapps/schedules/tests/test_signals.py @@ -1,14 +1,12 @@ -from collections import namedtuple import datetime import ddt -from enum import Enum from mock import patch from pytz import utc from course_modes.models import CourseMode from course_modes.tests.factories import CourseModeFactory from courseware.models import DynamicUpgradeDeadlineConfiguration -from openedx.core.djangoapps.schedules.signals import SCHEDULE_WAFFLE_FLAG +from openedx.core.djangoapps.schedules.signals import CREATE_SCHEDULE_WAFFLE_FLAG from openedx.core.djangoapps.site_configuration.tests.factories import SiteFactory from openedx.core.djangoapps.waffle_utils.testutils import override_waffle_flag from openedx.core.djangolib.testing.utils import skip_unless_lms @@ -37,40 +35,40 @@ class CreateScheduleTests(SharedModuleStoreTestCase): with self.assertRaises(Schedule.DoesNotExist): enrollment.schedule - @override_waffle_flag(SCHEDULE_WAFFLE_FLAG, True) + @override_waffle_flag(CREATE_SCHEDULE_WAFFLE_FLAG, True) def test_create_schedule(self, mock_get_current_site): site = SiteFactory.create() mock_get_current_site.return_value = site ScheduleConfigFactory.create(site=site) self.assert_schedule_created() - @override_waffle_flag(SCHEDULE_WAFFLE_FLAG, True) + @override_waffle_flag(CREATE_SCHEDULE_WAFFLE_FLAG, True) def test_no_current_site(self, mock_get_current_site): mock_get_current_site.return_value = None self.assert_schedule_not_created() - @override_waffle_flag(SCHEDULE_WAFFLE_FLAG, True) + @override_waffle_flag(CREATE_SCHEDULE_WAFFLE_FLAG, True) def test_schedule_config_disabled_waffle_enabled(self, mock_get_current_site): site = SiteFactory.create() mock_get_current_site.return_value = site ScheduleConfigFactory.create(site=site, create_schedules=False) self.assert_schedule_created() - @override_waffle_flag(SCHEDULE_WAFFLE_FLAG, False) + @override_waffle_flag(CREATE_SCHEDULE_WAFFLE_FLAG, False) def test_schedule_config_enabled_waffle_disabled(self, mock_get_current_site): site = SiteFactory.create() mock_get_current_site.return_value = site ScheduleConfigFactory.create(site=site, create_schedules=True) self.assert_schedule_created() - @override_waffle_flag(SCHEDULE_WAFFLE_FLAG, False) + @override_waffle_flag(CREATE_SCHEDULE_WAFFLE_FLAG, False) def test_schedule_config_disabled_waffle_disabled(self, mock_get_current_site): site = SiteFactory.create() mock_get_current_site.return_value = site ScheduleConfigFactory.create(site=site, create_schedules=False) self.assert_schedule_not_created() - @override_waffle_flag(SCHEDULE_WAFFLE_FLAG, True) + @override_waffle_flag(CREATE_SCHEDULE_WAFFLE_FLAG, True) def test_schedule_config_creation_enabled_instructor_paced(self, mock_get_current_site): site = SiteFactory.create() mock_get_current_site.return_value = site diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt index 135d2dffb5..ad62812529 100644 --- a/requirements/edx/base.txt +++ b/requirements/edx/base.txt @@ -38,7 +38,7 @@ django==1.8.18 django-waffle==0.12.0 djangorestframework-jwt==1.11.0 enum34==1.1.6 -edx-ace==0.1.2 +edx-ace==0.1.4 edx-ccx-keys==0.2.1 edx-celeryutils==0.2.6 edx-drf-extensions==1.2.3