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:
@@ -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
|
||||
|
||||
|
||||
@@ -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."""
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user