Merge pull request #27668 from edx/ddumesnil/end-date-update-aa-686

feat: Update the course end date block display logic
This commit is contained in:
Dillon Dumesnil
2021-05-19 08:49:56 -07:00
committed by GitHub
3 changed files with 48 additions and 43 deletions

View File

@@ -24,7 +24,6 @@ from lms.djangoapps.certificates.api import get_active_web_certificate
from lms.djangoapps.courseware.utils import verified_upgrade_deadline_link, can_show_verified_upgrade
from lms.djangoapps.verify_student.models import VerificationDeadline
from lms.djangoapps.verify_student.services import IDVerificationService
from openedx.core.djangoapps.catalog.utils import get_course_run_details
from openedx.core.djangoapps.certificates.api import can_show_certificate_available_date_field
from openedx.core.djangolib.markup import HTML, Text
from openedx.features.course_duration_limits.access import get_user_course_expiration_date
@@ -280,7 +279,7 @@ class CourseStartDate(DateSummary):
enrollment = CourseEnrollment.get_enrollment(self.user, self.course_id)
if enrollment and self.course.end and enrollment.created > self.course.end:
return ugettext_lazy('Enrollment Date')
return ugettext_lazy('Course Starts')
return ugettext_lazy('Course starts')
def register_alerts(self, request, course):
"""
@@ -317,28 +316,44 @@ class CourseEndDate(DateSummary):
Displays the end date of the course.
"""
css_class = 'end-date'
title = ugettext_lazy('Course End')
title = ugettext_lazy('Course ends')
is_enabled = True
@property
def description(self):
if self.current_time <= self.date:
"""
Returns a description for what experience changes a learner encounters when the course end date passes.
Note that this currently contains 4 scenarios:
1. End date is in the future and learner is enrolled in a certificate earning mode
2. End date is in the future and learner is not enrolled at all or not enrolled
in a certificate earning mode
3. End date is in the past
4. End date does not exist (and now neither does the description)
"""
if self.date and self.current_time <= self.date:
mode, is_active = CourseEnrollment.enrollment_mode_for_user(self.user, self.course_id)
if is_active and CourseMode.is_eligible_for_certificate(mode):
return _('To earn a certificate, you must complete all requirements before this date.')
return _('This course will be archived, which means you can review the course content '
'but can no longer participate in graded assignments or earn a certificate.')
else:
return _('After this date, course content will be archived.')
return _('This course is archived, which means you can review course content but it is no longer active.')
return _('After the course ends, the course content will be archived and no longer active.')
elif self.date:
return _('This course is archived, which means you can review course content but it is no longer active.')
else:
return ''
@property
def date(self):
"""
Returns the course end date, if applicable.
For self-paced courses using Personalized Learner Schedules, the end date is only displayed
if it is within 365 days.
"""
if self.course.self_paced and RELATIVE_DATES_FLAG.is_enabled(self.course_id):
weeks_to_complete = get_course_run_details(self.course.id, ['weeks_to_complete']).get('weeks_to_complete')
if weeks_to_complete:
course_duration = datetime.timedelta(weeks=weeks_to_complete)
if self.course.end and self.course.end < (self.current_time + course_duration):
return self.course.end
return None
one_year = datetime.timedelta(days=365)
if self.course.end and self.course.end < (self.current_time + one_year):
return self.course.end
return None
return self.course.end

View File

@@ -4,7 +4,6 @@
from datetime import datetime, timedelta
from unittest.mock import patch
import crum
import ddt
import waffle # lint-amnesty, pylint: disable=invalid-django-waffle-import
@@ -12,11 +11,11 @@ from django.contrib.messages.middleware import MessageMiddleware
from django.test import RequestFactory
from django.urls import reverse
from edx_toggles.toggles.testutils import override_waffle_flag
from freezegun import freeze_time
from pytz import utc
from common.djangoapps.course_modes.models import CourseMode
from common.djangoapps.course_modes.tests.factories import CourseModeFactory
from freezegun import freeze_time # lint-amnesty, pylint: disable=wrong-import-order
from lms.djangoapps.commerce.models import CommerceConfiguration
from lms.djangoapps.course_home_api.toggles import COURSE_HOME_MICROFRONTEND, COURSE_HOME_MICROFRONTEND_DATES_TAB
from lms.djangoapps.courseware.courses import get_course_date_blocks
@@ -42,7 +41,6 @@ from lms.djangoapps.verify_student.tests.factories import SoftwareSecurePhotoVer
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from openedx.core.djangoapps.content.course_overviews.tests.factories import CourseOverviewFactory
from openedx.core.djangoapps.self_paced.models import SelfPacedConfiguration
from openedx.core.djangoapps.site_configuration.tests.factories import SiteFactory # pylint: disable=unused-import
from openedx.core.djangoapps.user_api.preferences.api import set_user_preference
from openedx.features.course_duration_limits.models import CourseDurationLimitConfig
from openedx.features.course_experience import (
@@ -114,7 +112,7 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
{'verification_status': 'expired'},
(TodaysDate, CourseEndDate, VerificationDeadlineDate)),
# Verified enrollment with `approved` photo-verification during course run
({'days_till_start': -10, },
({'days_till_start': -10},
{'verification_status': 'approved'},
(TodaysDate, CourseEndDate)),
# Verified enrollment with *NO* course end date
@@ -522,15 +520,16 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
user = create_user()
CourseEnrollmentFactory(course_id=course.id, user=user, mode=CourseMode.VERIFIED)
block = CourseEndDate(course, user)
assert block.description == 'To earn a certificate, you must complete all requirements before this date.'
assert block.description == ('This course will be archived, which means you can review the course content '
'but can no longer participate in graded assignments or earn a certificate.')
def test_course_end_date_for_non_certificate_eligible_mode(self):
course = create_course_run(days_till_start=-1)
user = create_user()
CourseEnrollmentFactory(course_id=course.id, user=user, mode=CourseMode.AUDIT)
block = CourseEndDate(course, user)
assert block.description == 'After this date, course content will be archived.'
assert block.title == 'Course End'
assert block.description == 'After the course ends, the course content will be archived and no longer active.'
assert block.title == 'Course ends'
def test_course_end_date_after_course(self):
course = create_course_run(days_till_start=-2, days_till_end=-1)
@@ -539,35 +538,26 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
block = CourseEndDate(course, user)
assert block.description ==\
'This course is archived, which means you can review course content but it is no longer active.'
assert block.title == 'Course End'
assert block.title == 'Course ends'
@ddt.data(
{'weeks_to_complete': 7}, # Weeks to complete > time til end (end date shown)
{'weeks_to_complete': 4}, # Weeks to complete < time til end (end date not shown)
)
@ddt.data(300, 400)
@override_waffle_flag(RELATIVE_DATES_FLAG, active=True)
def test_course_end_date_self_paced(self, cr_details):
def test_course_end_date_self_paced(self, days_till_end):
"""
In self-paced courses, the end date will now only show up if the learner
views the course within the course's weeks to complete (as defined in
the course-discovery service). E.g. if the weeks to complete is 5 weeks
and the course doesn't end for 10 weeks, there will be no end date, but
if the course ends in 3 weeks, the end date will appear.
In self-paced courses, the end date will only show up if the learner
views the course within 365 days of the course end date.
"""
now = datetime.now(utc)
end_timedelta_number = 5
course = CourseFactory.create(
start=now + timedelta(days=-7), end=now + timedelta(weeks=end_timedelta_number), self_paced=True)
start=now + timedelta(days=-7), end=now + timedelta(days=days_till_end), self_paced=True)
user = create_user()
self.make_request(user)
with patch('lms.djangoapps.courseware.date_summary.get_course_run_details') as mock_get_cr_details:
mock_get_cr_details.return_value = cr_details
block = CourseEndDate(course, user)
assert block.title == 'Course End'
if cr_details['weeks_to_complete'] > end_timedelta_number:
assert block.date == course.end
else:
assert block.date is None
block = CourseEndDate(course, user)
assert block.title == 'Course ends'
if 365 > days_till_end:
assert block.date == course.end
else:
assert block.date is None
assert block.description == ''
def test_ecommerce_checkout_redirect(self):
"""Verify the block link redirects to ecommerce checkout if it's enabled."""

View File

@@ -41,7 +41,7 @@ class TestCourseDatesFragmentView(ModuleStoreTestCase):
def test_course_dates_fragment(self):
response = self.client.get(self.dates_fragment_url)
self.assertContains(response, 'Course End')
self.assertContains(response, 'Course ends')
self.client.logout()
response = self.client.get(self.dates_fragment_url)