Merge pull request #16092 from edx/upsell-nudges-for-unverified-users
Upsell nudges for unverified users
This commit is contained in:
@@ -1696,16 +1696,13 @@ class CourseEnrollment(models.Model):
|
||||
def verified_mode(self):
|
||||
return CourseMode.verified_mode_for_course(self.course_id)
|
||||
|
||||
@property
|
||||
@cached_property
|
||||
def upgrade_deadline(self):
|
||||
"""
|
||||
Returns the upgrade deadline for this enrollment, if it is upgradeable.
|
||||
|
||||
If the seat cannot be upgraded, None is returned.
|
||||
|
||||
Note:
|
||||
When loading this model, use `select_related` to retrieve the associated schedule object.
|
||||
|
||||
Returns:
|
||||
datetime|None
|
||||
"""
|
||||
@@ -1717,40 +1714,61 @@ class CourseEnrollment(models.Model):
|
||||
)
|
||||
return None
|
||||
|
||||
if self.dynamic_upgrade_deadline is not None:
|
||||
return self.dynamic_upgrade_deadline
|
||||
|
||||
return self.course_upgrade_deadline
|
||||
|
||||
@cached_property
|
||||
def dynamic_upgrade_deadline(self):
|
||||
|
||||
try:
|
||||
schedule_driven_deadlines_enabled = (
|
||||
DynamicUpgradeDeadlineConfiguration.is_enabled()
|
||||
or CourseDynamicUpgradeDeadlineConfiguration.is_enabled(self.course_id)
|
||||
course_overview = self.course
|
||||
except CourseOverview.DoesNotExist:
|
||||
course_overview = self.course_overview
|
||||
|
||||
if not course_overview.self_paced:
|
||||
return None
|
||||
|
||||
if not DynamicUpgradeDeadlineConfiguration.is_enabled():
|
||||
return None
|
||||
|
||||
course_config = CourseDynamicUpgradeDeadlineConfiguration.current(self.course_id)
|
||||
if course_config.enabled and course_config.opt_out:
|
||||
return None
|
||||
|
||||
try:
|
||||
if not self.schedule:
|
||||
return None
|
||||
|
||||
log.debug(
|
||||
'Schedules: Pulling upgrade deadline for CourseEnrollment %d from Schedule %d.',
|
||||
self.id, self.schedule.id
|
||||
)
|
||||
if (
|
||||
schedule_driven_deadlines_enabled
|
||||
and self.course_overview.self_paced
|
||||
and self.schedule
|
||||
and self.schedule.upgrade_deadline is not None
|
||||
):
|
||||
log.debug(
|
||||
'Schedules: Pulling upgrade deadline for CourseEnrollment %d from Schedule %d.',
|
||||
self.id, self.schedule.id
|
||||
)
|
||||
return self.schedule.upgrade_deadline
|
||||
upgrade_deadline = self.schedule.upgrade_deadline
|
||||
except ObjectDoesNotExist:
|
||||
# NOTE: Schedule has a one-to-one mapping with CourseEnrollment. If no schedule is associated
|
||||
# with this enrollment, Django will raise an exception rather than return None.
|
||||
log.debug('Schedules: No schedule exists for CourseEnrollment %d.', self.id)
|
||||
pass
|
||||
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):
|
||||
try:
|
||||
if self.verified_mode:
|
||||
log.debug('Schedules: Defaulting to verified mode expiration date-time for %s.', self.course_id)
|
||||
return self.verified_mode.expiration_datetime
|
||||
else:
|
||||
log.debug('Schedules: No verified mode located for %s.', self.course_id)
|
||||
return None
|
||||
except CourseMode.DoesNotExist:
|
||||
log.debug('Schedules: %s has no verified mode.', self.course_id)
|
||||
pass
|
||||
|
||||
log.debug('Schedules: Returning default of `None`')
|
||||
return None
|
||||
return None
|
||||
|
||||
def is_verified_enrollment(self):
|
||||
"""
|
||||
|
||||
@@ -14,6 +14,7 @@ from django.db.models.functions import Lower
|
||||
from course_modes.models import CourseMode
|
||||
from course_modes.tests.factories import CourseModeFactory
|
||||
from courseware.models import DynamicUpgradeDeadlineConfiguration
|
||||
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
|
||||
from openedx.core.djangoapps.schedules.models import Schedule
|
||||
from openedx.core.djangoapps.schedules.tests.factories import ScheduleFactory
|
||||
from openedx.core.djangolib.testing.utils import skip_unless_lms
|
||||
@@ -132,6 +133,25 @@ class CourseEnrollmentTests(SharedModuleStoreTestCase):
|
||||
self.assertEqual(Schedule.objects.all().count(), 0)
|
||||
self.assertEqual(enrollment.upgrade_deadline, course_mode.expiration_datetime)
|
||||
|
||||
@skip_unless_lms
|
||||
# NOTE: We mute the post_save signal to prevent Schedules from being created for new enrollments
|
||||
@factory.django.mute_signals(signals.post_save)
|
||||
def test_upgrade_deadline_with_schedule(self):
|
||||
""" The property should use either the CourseMode or related Schedule to determine the deadline. """
|
||||
course = CourseFactory(self_paced=True)
|
||||
CourseModeFactory(
|
||||
course_id=course.id,
|
||||
mode_slug=CourseMode.VERIFIED,
|
||||
# This must be in the future to ensure it is returned by downstream code.
|
||||
expiration_datetime=datetime.datetime.now(pytz.UTC) + datetime.timedelta(days=30),
|
||||
)
|
||||
course_overview = CourseOverview.load_from_module_store(course.id)
|
||||
enrollment = CourseEnrollmentFactory(
|
||||
course_id=course.id,
|
||||
mode=CourseMode.AUDIT,
|
||||
course=course_overview,
|
||||
)
|
||||
|
||||
# The schedule's upgrade deadline should be used if a schedule exists
|
||||
DynamicUpgradeDeadlineConfiguration.objects.create(enabled=True)
|
||||
schedule = ScheduleFactory(enrollment=enrollment)
|
||||
|
||||
Reference in New Issue
Block a user