diff --git a/common/djangoapps/student/models.py b/common/djangoapps/student/models.py index 39fd943eff..4d51f91486 100644 --- a/common/djangoapps/student/models.py +++ b/common/djangoapps/student/models.py @@ -1709,25 +1709,13 @@ class CourseEnrollment(models.Model): return None if self.dynamic_upgrade_deadline is not None: - # When course modes expire they aren't found any more and None would be returned. - # Replicate that behavior here by returning None if the personalized deadline is in the past. - if datetime.now(UTC) >= self.dynamic_upgrade_deadline: - return None return self.dynamic_upgrade_deadline return self.course_upgrade_deadline @cached_property def dynamic_upgrade_deadline(self): - """ - Returns the learner's personalized upgrade deadline if one exists, otherwise it returns None. - Note that this will return a value even if the deadline is in the past. This property can be used - to modify behavior for users with personalized deadlines by checking if it's None or not. - - Returns: - datetime|None - """ try: course_overview = self.course except CourseOverview.DoesNotExist: @@ -1758,19 +1746,13 @@ class CourseEnrollment(models.Model): log.debug('Schedules: No schedule exists for CourseEnrollment %d.', self.id) return None + if upgrade_deadline is None or datetime.now(UTC) >= upgrade_deadline: + return None + return upgrade_deadline @cached_property def course_upgrade_deadline(self): - """ - Returns the expiration datetime for the verified course mode. - - If the mode is already expired, return None. Also return None if the course does not have a verified - course mode. - - Returns: - datetime|None - """ try: if self.verified_mode: log.debug('Schedules: Defaulting to verified mode expiration date-time for %s.', self.course_id) diff --git a/lms/djangoapps/courseware/course_tools.py b/lms/djangoapps/courseware/course_tools.py deleted file mode 100644 index 0ca6708bf4..0000000000 --- a/lms/djangoapps/courseware/course_tools.py +++ /dev/null @@ -1,73 +0,0 @@ -""" -Platform plugins to support a verified upgrade tool. -""" - -import datetime -import pytz -from django.utils.translation import ugettext as _ - -from course_modes.models import CourseMode -from openedx.features.course_experience.course_tools import CourseTool -from student.models import CourseEnrollment -from courseware.date_summary import verified_upgrade_deadline_link -from request_cache import get_request - - -class VerifiedUpgradeTool(CourseTool): - """ - The verified upgrade tool. - """ - @classmethod - def analytics_id(cls): - """ - Returns an id to uniquely identify this tool in analytics events. - """ - return 'edx.tool.verified_upgrade' - - @classmethod - def is_enabled(cls, request, course_key): - """ - Show this tool to all learners who are eligible to upgrade. - """ - enrollment = CourseEnrollment.get_enrollment(request.user, course_key) - if enrollment is None: - return False - - if enrollment.dynamic_upgrade_deadline is None: - return False - - if not enrollment.is_active: - return False - - if enrollment.mode not in CourseMode.UPSELL_TO_VERIFIED_MODES: - return False - - if enrollment.course_upgrade_deadline is None: - return False - - if datetime.datetime.now(pytz.UTC) >= enrollment.course_upgrade_deadline: - return False - - return True - - @classmethod - def title(cls): - """ - Returns the title of this tool. - """ - return _('Upgrade to Verified') - - @classmethod - def icon_classes(cls): - """ - Returns the icon classes needed to represent this tool. - """ - return 'fa fa-certificate' - - @classmethod - def url(cls, course_key): - """ - Returns the URL for this tool for the specified course key. - """ - request = get_request() - return verified_upgrade_deadline_link(request.user, course_id=course_key) diff --git a/lms/djangoapps/courseware/date_summary.py b/lms/djangoapps/courseware/date_summary.py index 1bf76a47ba..b885beaf7c 100644 --- a/lms/djangoapps/courseware/date_summary.py +++ b/lms/djangoapps/courseware/date_summary.py @@ -10,7 +10,6 @@ from babel.dates import format_timedelta from django.conf import settings from django.core.urlresolvers import reverse -from django.utils.formats import date_format from django.utils.functional import cached_property from django.utils.translation import get_language, to_locale, ugettext_lazy from django.utils.translation import ugettext as _ @@ -445,6 +444,11 @@ class VerifiedUpgradeDeadlineDate(DateSummary): Verified track. """ css_class = 'verified-upgrade-deadline' + title = ugettext_lazy('Verification Upgrade Deadline') + description = ugettext_lazy( + 'You are still eligible to upgrade to a Verified Certificate! ' + 'Pursue it to highlight the knowledge and skills you gain in this course.' + ) link_text = ugettext_lazy('Upgrade to Verified Certificate') @property @@ -471,49 +475,12 @@ class VerifiedUpgradeDeadlineDate(DateSummary): @lazy def date(self): + deadline = None + if self.enrollment: - return self.enrollment.upgrade_deadline - else: - return None + deadline = self.enrollment.upgrade_deadline - @property - def title(self): - dynamic_deadline = self._dynamic_deadline() - if dynamic_deadline is not None: - return _('Upgrade to Verified Certificate') - - return _('Verification Upgrade Deadline') - - def _dynamic_deadline(self): - if not self.enrollment: - return None - - return self.enrollment.dynamic_upgrade_deadline - - @property - def description(self): - dynamic_deadline = self._dynamic_deadline() - if dynamic_deadline is not None: - return _('Don\'t miss the opportunity to highlight your new knowledge and skills by earning a verified' - ' certificate.') - - return _('You are still eligible to upgrade to a Verified Certificate! ' - 'Pursue it to highlight the knowledge and skills you gain in this course.') - - @property - def relative_datestring(self): - dynamic_deadline = self._dynamic_deadline() - if dynamic_deadline is None: - return super(VerifiedUpgradeDeadlineDate, self).relative_datestring - - if self.date is None or self.deadline_has_passed(): - return ' ' - - # Translators: This describes the time by which the user - # should upgrade to the verified track. 'date' will be - # their personalized verified upgrade deadline formatted - # according to their locale. - return _(u'by {date}') + return deadline def register_alerts(self, request, course): """ @@ -524,13 +491,6 @@ class VerifiedUpgradeDeadlineDate(DateSummary): return days_left_to_upgrade = (self.date - self.current_time).days if self.date > self.current_time and days_left_to_upgrade <= settings.COURSE_MESSAGE_ALERT_DURATION_IN_DAYS: - upgrade_message = _( - "Don't forget, you have {time_remaining_string} left to upgrade to a Verified Certificate." - ).format(time_remaining_string=self.time_remaining_string) - if self._dynamic_deadline() is not None: - upgrade_message = _( - "Don't forget to upgrade to a verified certificate by {localized_date}." - ).format(localized_date=date_format(self.date)) CourseHomeMessages.register_info_message( request, Text(_( @@ -550,7 +510,11 @@ class VerifiedUpgradeDeadlineDate(DateSummary): upgrade_label=Text(_('Upgrade ({upgrade_price})')).format(upgrade_price=upgrade_price), ) ), - title=Text(upgrade_message) + title=Text(_( + "Don't forget, you have {time_remaining_string} left to upgrade to a Verified Certificate." + )).format( + time_remaining_string=self.time_remaining_string, + ) ) diff --git a/lms/djangoapps/courseware/tests/test_course_info.py b/lms/djangoapps/courseware/tests/test_course_info.py index 9923820cdf..45b91bfe97 100644 --- a/lms/djangoapps/courseware/tests/test_course_info.py +++ b/lms/djangoapps/courseware/tests/test_course_info.py @@ -379,7 +379,7 @@ class SelfPacedCourseInfoTestCase(LoginEnrollmentTestCase, SharedModuleStoreTest self.assertEqual(resp.status_code, 200) def test_num_queries_instructor_paced(self): - self.fetch_course_info_with_queries(self.instructor_paced_course, 27, 3) + self.fetch_course_info_with_queries(self.instructor_paced_course, 26, 3) def test_num_queries_self_paced(self): - self.fetch_course_info_with_queries(self.self_paced_course, 27, 3) + self.fetch_course_info_with_queries(self.self_paced_course, 26, 3) diff --git a/lms/djangoapps/courseware/tests/test_course_tools.py b/lms/djangoapps/courseware/tests/test_course_tools.py deleted file mode 100644 index cbe9ff7f69..0000000000 --- a/lms/djangoapps/courseware/tests/test_course_tools.py +++ /dev/null @@ -1,99 +0,0 @@ -import datetime - -from mock import patch -from nose.plugins.attrib import attr -import pytz -from django.test import RequestFactory - -from course_modes.models import CourseMode -from course_modes.tests.factories import CourseModeFactory -from courseware.course_tools import VerifiedUpgradeTool -from courseware.models import DynamicUpgradeDeadlineConfiguration -from openedx.core.djangoapps.content.course_overviews.models import CourseOverview -from openedx.core.djangoapps.schedules.config 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 student.tests.factories import CourseEnrollmentFactory, UserFactory -from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase -from xmodule.modulestore.tests.factories import CourseFactory - - -@attr(shard=3) -class VerifiedUpgradeToolTest(SharedModuleStoreTestCase): - - @classmethod - def setUpClass(cls): - super(VerifiedUpgradeToolTest, cls).setUpClass() - cls.now = datetime.datetime.now(pytz.UTC) - - cls.course = CourseFactory.create( - org='edX', - number='test', - display_name='Test Course', - self_paced=True, - start=cls.now - datetime.timedelta(days=30), - ) - cls.course_overview = CourseOverview.get_from_id(cls.course.id) - - @override_waffle_flag(CREATE_SCHEDULE_WAFFLE_FLAG, True) - def setUp(self): - super(VerifiedUpgradeToolTest, self).setUp() - - self.course_verified_mode = CourseModeFactory( - course_id=self.course.id, - mode_slug=CourseMode.VERIFIED, - expiration_datetime=self.now + datetime.timedelta(days=30), - ) - - patcher = patch('openedx.core.djangoapps.schedules.signals.get_current_site') - mock_get_current_site = patcher.start() - self.addCleanup(patcher.stop) - mock_get_current_site.return_value = SiteFactory.create() - - DynamicUpgradeDeadlineConfiguration.objects.create(enabled=True) - - self.enrollment = CourseEnrollmentFactory( - course_id=self.course.id, - mode=CourseMode.AUDIT, - course=self.course_overview, - ) - self.request = RequestFactory().request() - self.request.user = self.enrollment.user - - def test_tool_visible(self): - self.assertTrue(VerifiedUpgradeTool().is_enabled(self.request, self.course.id)) - - def test_not_visible_when_no_enrollment_exists(self): - self.enrollment.delete() - - request = RequestFactory().request() - request.user = UserFactory() - self.assertFalse(VerifiedUpgradeTool().is_enabled(self.request, self.course.id)) - - def test_not_visible_when_using_deadline_from_course_mode(self): - DynamicUpgradeDeadlineConfiguration.objects.create(enabled=False) - self.assertFalse(VerifiedUpgradeTool().is_enabled(self.request, self.course.id)) - - def test_not_visible_when_enrollment_is_inactive(self): - self.enrollment.is_active = False - self.enrollment.save() - self.assertFalse(VerifiedUpgradeTool().is_enabled(self.request, self.course.id)) - - def test_not_visible_when_already_verified(self): - self.enrollment.mode = CourseMode.VERIFIED - self.enrollment.save() - self.assertFalse(VerifiedUpgradeTool().is_enabled(self.request, self.course.id)) - - def test_not_visible_when_no_verified_track(self): - self.course_verified_mode.delete() - self.assertFalse(VerifiedUpgradeTool().is_enabled(self.request, self.course.id)) - - def test_not_visible_when_course_deadline_has_passed(self): - self.course_verified_mode.expiration_datetime = self.now - datetime.timedelta(days=1) - self.course_verified_mode.save() - self.assertFalse(VerifiedUpgradeTool().is_enabled(self.request, self.course.id)) - - def test_not_visible_when_course_mode_has_no_deadline(self): - self.course_verified_mode.expiration_datetime = None - self.course_verified_mode.save() - self.assertFalse(VerifiedUpgradeTool().is_enabled(self.request, self.course.id)) diff --git a/lms/djangoapps/courseware/tests/test_date_summary.py b/lms/djangoapps/courseware/tests/test_date_summary.py index 3dcb787e39..340e7883a6 100644 --- a/lms/djangoapps/courseware/tests/test_date_summary.py +++ b/lms/djangoapps/courseware/tests/test_date_summary.py @@ -585,16 +585,6 @@ class TestScheduleOverrides(SharedModuleStoreTestCase): enrollment = CourseEnrollmentFactory(course_id=course.id, mode=CourseMode.AUDIT) block = VerifiedUpgradeDeadlineDate(course, enrollment.user) self.assertEqual(block.date, expected) - self._check_text(block) - - def _check_text(self, upgrade_date_summary): - self.assertEqual(upgrade_date_summary.title, 'Upgrade to Verified Certificate') - self.assertEqual( - upgrade_date_summary.description, - 'Don\'t miss the opportunity to highlight your new knowledge and skills by earning a verified' - ' certificate.' - ) - self.assertEqual(upgrade_date_summary.relative_datestring, 'by {date}') @override_waffle_flag(CREATE_SCHEDULE_WAFFLE_FLAG, True) def test_date_with_self_paced_with_enrollment_after_course_start(self): diff --git a/lms/djangoapps/courseware/tests/test_views.py b/lms/djangoapps/courseware/tests/test_views.py index 9c71159ef5..b65169d5e5 100644 --- a/lms/djangoapps/courseware/tests/test_views.py +++ b/lms/djangoapps/courseware/tests/test_views.py @@ -213,8 +213,8 @@ class IndexQueryTestCase(ModuleStoreTestCase): NUM_PROBLEMS = 20 @ddt.data( - (ModuleStoreEnum.Type.mongo, 10, 143), - (ModuleStoreEnum.Type.split, 4, 143), + (ModuleStoreEnum.Type.mongo, 10, 145), + (ModuleStoreEnum.Type.split, 4, 145), ) @ddt.unpack def test_index_query_counts(self, store_type, expected_mongo_query_count, expected_mysql_query_count): diff --git a/openedx/features/course_experience/tests/views/test_course_home.py b/openedx/features/course_experience/tests/views/test_course_home.py index bdbdb6969c..2b9eab729b 100644 --- a/openedx/features/course_experience/tests/views/test_course_home.py +++ b/openedx/features/course_experience/tests/views/test_course_home.py @@ -244,7 +244,7 @@ class TestCourseHomePageAccess(CourseHomePageTestCase): expected_count = 1 if (is_enrolled or is_unenrolled_staff) else 0 self.assertContains(response, TEST_CHAPTER_NAME, count=expected_count) self.assertContains(response, 'Start Course', count=expected_count) - self.assertContains(response, 'Learn About Verified Certificate', count=(1 if is_enrolled else 0)) + self.assertContains(response, 'Learn About Verified Certificate', count=expected_count) self.assertContains(response, TEST_WELCOME_MESSAGE, count=expected_count) # Verify that the expected message is shown to the user @@ -285,7 +285,7 @@ class TestCourseHomePageAccess(CourseHomePageTestCase): expected_count = 1 if (is_enrolled or is_unenrolled_staff) else 0 self.assertContains(response, TEST_CHAPTER_NAME, count=expected_count) self.assertContains(response, 'Start Course', count=expected_count) - self.assertContains(response, 'Learn About Verified Certificate', count=(1 if is_enrolled else 0)) + self.assertContains(response, 'Learn About Verified Certificate', count=expected_count) # Verify that the expected message is shown to the user self.assertContains(response, '