Allow entitlements to be used past course has ended.

Allow entitlements to be used past the course has ended but
upgrade deadline is still in future for already enrolled
learners.

PROD-1497
This commit is contained in:
Waheed Ahmed
2020-08-06 15:24:20 +05:00
parent 3a3d3a8766
commit 13b3764f20
8 changed files with 36 additions and 32 deletions

View File

@@ -970,23 +970,22 @@ class EntitlementEnrollmentViewSetTest(ModuleStoreTestCase):
assert course_entitlement.enrollment_course_run is not None
@patch("entitlements.rest_api.v1.views.get_course_runs_for_course")
def test_enrollment_closed_upgrade_open(self, mock_get_course_runs):
def test_already_enrolled_course_ended(self, mock_get_course_runs):
"""
Test that user can still select a session while course enrollment
is closed and upgrade deadline is in future.
Test that already enrolled user can still select a session while
course has ended but upgrade deadline is in future.
"""
course_entitlement = CourseEntitlementFactory.create(user=self.user, mode=CourseMode.VERIFIED)
mock_get_course_runs.return_value = self.return_values
# Setup enrollment period to be in the past
utc_now = datetime.now(UTC)
self.course.enrollment_start = utc_now - timedelta(days=15)
self.course.enrollment_end = utc_now - timedelta(days=1)
self.course.start = utc_now - timedelta(days=15)
self.course.end = utc_now - timedelta(days=1)
self.course = self.update_course(self.course, self.user.id)
CourseOverview.update_select_courses([self.course.id], force_update=True)
assert CourseEnrollment.is_enrollment_closed(self.user, self.course)
assert not CourseEnrollment.is_enrolled(self.user, self.course.id)
CourseEnrollment.enroll(self.user, self.course.id, mode=CourseMode.AUDIT)
url = reverse(
self.ENTITLEMENTS_ENROLLMENT_NAMESPACE,
@@ -1005,6 +1004,8 @@ class EntitlementEnrollmentViewSetTest(ModuleStoreTestCase):
assert response.status_code == 201
assert CourseEnrollment.is_enrolled(self.user, self.course.id)
(enrolled_mode, is_active) = CourseEnrollment.enrollment_mode_for_user(self.user, self.course.id)
assert is_active and (enrolled_mode == course_entitlement.mode)
assert course_entitlement.enrollment_course_run is not None
@patch("entitlements.rest_api.v1.views.get_course_runs_for_course")

View File

@@ -155,14 +155,15 @@ class TestCourseRunFulfillableForEntitlement(ModuleStoreTestCase):
assert not is_course_run_entitlement_fulfillable(course_overview.id, entitlement)
def test_course_run_fulfillable_enrollment_ended_upgrade_open(self):
def test_course_run_fulfillable_already_enrolled_course_ended(self):
course_overview = self.create_course(
start_from_now=-3,
end_from_now=2,
end_from_now=-1,
enrollment_start_from_now=-2,
enrollment_end_from_now=-1,
)
entitlement = CourseEntitlementFactory.create(mode=CourseMode.VERIFIED)
CourseEnrollmentFactory.create(user=entitlement.user, course_id=course_overview.id)
assert is_course_run_entitlement_fulfillable(course_overview.id, entitlement)

View File

@@ -22,9 +22,8 @@ def is_course_run_entitlement_fulfillable(
"""
Checks that the current run meets the following criteria for an entitlement
1) Is currently running or start in the future
2) A User can enroll in or is currently enrolled
3) A User can upgrade to the entitlement mode
1) A User can enroll in or is currently enrolled
2) A User can upgrade to the entitlement mode
Arguments:
course_run_key (CourseKey): The id of the Course run that is being checked.
@@ -43,16 +42,13 @@ def is_course_run_entitlement_fulfillable(
))
return False
# Verify that the course is still running
run_start = course_overview.start
run_end = course_overview.end
is_running = run_start and (not run_end or (run_end and (run_end > compare_date)))
# Verify that the course run can currently be enrolled, explicitly
# not checking for enrollment end date becasue course run is still
# fulfillable if enrollment has ended but VUD is in future
# Verify that the course run can currently be enrolled
enrollment_start = course_overview.enrollment_start
can_enroll = not enrollment_start or enrollment_start < compare_date
enrollment_end = course_overview.enrollment_end
can_enroll = (
(not enrollment_start or enrollment_start < compare_date)
and (not enrollment_end or enrollment_end > compare_date)
)
# Is the user already enrolled in the Course Run
is_enrolled = CourseEnrollment.is_enrolled(entitlement.user, course_run_key)
@@ -61,4 +57,4 @@ def is_course_run_entitlement_fulfillable(
unexpired_paid_modes = [mode.slug for mode in CourseMode.paid_modes_for_course(course_run_key)]
can_upgrade = unexpired_paid_modes and entitlement.mode in unexpired_paid_modes
return is_running and can_upgrade and (is_enrolled or can_enroll)
return course_overview.start and can_upgrade and (is_enrolled or can_enroll)

View File

@@ -169,7 +169,7 @@ describe('Course Card View', () => {
);
});
it('should show a message if an there is an upcoming course run', () => {
it('should show a message if there is an upcoming course run', () => {
course.course_runs[0].is_enrollment_open = false;
setupView(course, false);

View File

@@ -46,7 +46,9 @@ class CourseCardView extends Backbone.View {
const $upgradeMessage = this.$('.upgrade-message');
const $certStatus = this.$('.certificate-status');
const $expiredNotification = this.$('.expired-notification');
const courseKey = this.model.get('course_run_key');
const expired = this.model.get('expired');
const canUpgrade = this.model.get('upgrade_url') && !(expired === true);
const courseUUID = this.model.get('uuid');
const containerSelector = `#course-${courseUUID}`;
@@ -72,8 +74,7 @@ class CourseCardView extends Backbone.View {
enterCourseBtn: `${containerSelector} .view-course-button`,
availableSessions: JSON.stringify(this.model.get('course_runs')),
entitlementUUID: this.entitlement.uuid,
currentSessionId: this.model.isEnrolledInSession() ?
this.model.get('course_run_key') : null,
currentSessionId: this.model.isEnrolledInSession() && !canUpgrade ? courseKey : null,
enrollUrl: this.model.get('enroll_url'),
courseHomeUrl: this.model.get('course_url'),
expiredAt: this.entitlement.expired_at,
@@ -81,7 +82,7 @@ class CourseCardView extends Backbone.View {
});
}
if (this.model.get('upgrade_url') && !(expired === true)) {
if (canUpgrade) {
this.upgradeMessage = new UpgradeMessageView({
$el: $upgradeMessage,
model: this.model,

View File

@@ -141,7 +141,8 @@ class CourseEntitlementView extends Backbone.View {
this.trackSessionChange(eventPage, eventAction, prevSession);
// With a containing backbone view, we can simply re-render the parent card
if (this.$parentEl) {
if (this.$parentEl &&
this.courseCardModel.get('course_run_key') !== this.currentSessionSelection) {
this.courseCardModel.updateCourseRun(this.currentSessionSelection);
return;
}
@@ -388,6 +389,7 @@ class CourseEntitlementView extends Backbone.View {
sessionData.forEach((session) => {
Object.assign(session, {
enrollment_end: CourseEntitlementView.formatDate(session.enrollment_end, dateFormat),
session_id: session.session_id ? session.session_id : session.key,
session_dates: this.courseCardModel.formatDateString({
start_date: CourseEntitlementView.formatDate(session.start, dateFormat),
advertised_start: session.advertised_start,

View File

@@ -655,14 +655,19 @@ class TestSessionEntitlement(CatalogIntegrationMixin, TestCase):
def test_unpublished_sessions_for_entitlement(self, mock_get_edx_api_data):
"""
Test unpublished course runs are not part of visible session entitlements when the user
is not enrolled.
is not enrolled and upgrade deadline is passed.
"""
catalog_course_run = CourseRunFactory.create(status=COURSE_UNPUBLISHED)
catalog_course = CourseFactory(course_runs=[catalog_course_run])
mock_get_edx_api_data.return_value = catalog_course
course_key = CourseKey.from_string(catalog_course_run.get('key'))
course_overview = CourseOverviewFactory.create(id=course_key, start=self.tomorrow)
CourseModeFactory.create(mode_slug=CourseMode.VERIFIED, min_price=100, course_id=course_overview.id)
CourseModeFactory.create(
mode_slug=CourseMode.VERIFIED,
min_price=100,
course_id=course_overview.id,
expiration_datetime=now() - timedelta(days=1)
)
entitlement = CourseEntitlementFactory(
user=self.user, mode=CourseMode.VERIFIED
)

View File

@@ -553,9 +553,7 @@ def get_fulfillable_course_runs_for_entitlement(entitlement, course_runs):
# User is enrolled in the course so we should include it in the list of enrollable sessions always
# this will ensure it is available for the UI
enrollable_sessions.append(course_run)
elif (course_run.get('status') == COURSE_PUBLISHED and not
is_enrolled_in_mode and
is_course_run_entitlement_fulfillable(course_id, entitlement, search_time)):
elif not is_enrolled_in_mode and is_course_run_entitlement_fulfillable(course_id, entitlement, search_time):
enrollable_sessions.append(course_run)
enrollable_sessions.sort(key=lambda session: session.get('start'))