Added CourseEnrollment.upgrade_deadline
This new property knows how to rely on schedule deadliens and fallback to course mode deadlines, when necessary.
This commit is contained in:
committed by
Clinton Blackburn
parent
7274a20f38
commit
1e7f7df79b
@@ -134,6 +134,8 @@ class CourseMode(models.Model):
|
||||
)
|
||||
DEFAULT_MODE_SLUG = settings.COURSE_MODE_DEFAULTS['slug']
|
||||
|
||||
ALL_MODES = [AUDIT, CREDIT_MODE, HONOR, NO_ID_PROFESSIONAL_MODE, PROFESSIONAL, VERIFIED, ]
|
||||
|
||||
# Modes utilized for audit/free enrollments
|
||||
AUDIT_MODES = [AUDIT, HONOR]
|
||||
|
||||
@@ -489,6 +491,16 @@ class CourseMode(models.Model):
|
||||
"""
|
||||
return slug in [cls.PROFESSIONAL, cls.NO_ID_PROFESSIONAL_MODE]
|
||||
|
||||
@classmethod
|
||||
def is_mode_upgradeable(cls, mode_slug):
|
||||
"""
|
||||
Returns True if the given mode can be upgraded to another.
|
||||
|
||||
Note: Although, in practice, learners "upgrade" from verified to credit,
|
||||
that particular upgrade path is excluded by this method.
|
||||
"""
|
||||
return mode_slug in cls.AUDIT_MODES
|
||||
|
||||
@classmethod
|
||||
def is_verified_mode(cls, course_mode_tuple):
|
||||
"""Check whether the given modes is_verified or not.
|
||||
|
||||
@@ -1678,6 +1678,55 @@ class CourseEnrollment(models.Model):
|
||||
self._course_overview = None
|
||||
return self._course_overview
|
||||
|
||||
@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
|
||||
"""
|
||||
log.debug('Schedules: Determining upgrade deadline for CourseEnrollment %d...', self.id)
|
||||
if not CourseMode.is_mode_upgradeable(self.mode):
|
||||
log.debug(
|
||||
'Schedules: %s mode of %s is not upgradeable. Returning None for upgrade deadline.',
|
||||
self.mode, self.course_id
|
||||
)
|
||||
return None
|
||||
|
||||
try:
|
||||
if self.schedule:
|
||||
log.debug(
|
||||
'Schedules: Pulling upgrade deadline for CourseEnrollment %d from Schedule %d.',
|
||||
self.id, self.schedule.id
|
||||
)
|
||||
return 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
|
||||
|
||||
try:
|
||||
verified_mode = CourseMode.verified_mode_for_course(self.course_id)
|
||||
|
||||
if verified_mode:
|
||||
log.debug('Schedules: Defaulting to verified mode expiration date-time for %s.', self.course_id)
|
||||
return verified_mode.expiration_datetime
|
||||
else:
|
||||
log.debug('Schedules: No verified mode located for %s.', self.course_id)
|
||||
except CourseMode.DoesNotExist:
|
||||
log.debug('Schedules: %s has no verified mode.', self.course_id)
|
||||
pass
|
||||
|
||||
log.debug('Schedules: Returning default of `None`')
|
||||
return None
|
||||
|
||||
def is_verified_enrollment(self):
|
||||
"""
|
||||
Check the course enrollment mode is verified or not
|
||||
|
||||
@@ -1,17 +1,24 @@
|
||||
# pylint: disable=missing-docstring
|
||||
|
||||
import datetime
|
||||
import hashlib
|
||||
|
||||
import ddt
|
||||
import pytz
|
||||
from django.contrib.auth.models import AnonymousUser
|
||||
from django.core.cache import cache
|
||||
from django.db.models.functions import Lower
|
||||
|
||||
from course_modes.models import CourseMode
|
||||
from openedx.core.djangoapps.schedules.tests.factories import ScheduleFactory
|
||||
from openedx.core.djangolib.testing.utils import skip_unless_lms
|
||||
from student.models import CourseEnrollment
|
||||
from student.tests.factories import CourseEnrollmentFactory, UserFactory
|
||||
from student.tests.factories import CourseEnrollmentFactory, UserFactory, CourseModeFactory
|
||||
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class CourseEnrollmentTests(SharedModuleStoreTestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
@@ -20,8 +27,8 @@ class CourseEnrollmentTests(SharedModuleStoreTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(CourseEnrollmentTests, self).setUp()
|
||||
self.user = UserFactory.create()
|
||||
self.user_2 = UserFactory.create()
|
||||
self.user = UserFactory()
|
||||
self.user_2 = UserFactory()
|
||||
|
||||
def test_enrollment_status_hash_cache_key(self):
|
||||
username = 'test-user'
|
||||
@@ -103,3 +110,25 @@ class CourseEnrollmentTests(SharedModuleStoreTestCase):
|
||||
CourseEnrollment.objects.users_enrolled_in(self.course.id, include_inactive=True)
|
||||
)
|
||||
self.assertListEqual([self.user, self.user_2], all_enrolled_users)
|
||||
|
||||
@skip_unless_lms
|
||||
def test_upgrade_deadline(self):
|
||||
""" The property should use either the CourseMode or related Schedule to determine the deadline. """
|
||||
course_mode = CourseModeFactory(
|
||||
course_id=self.course.id,
|
||||
mode_slug=CourseMode.VERIFIED,
|
||||
expiration_datetime=datetime.datetime.now(pytz.UTC)
|
||||
)
|
||||
enrollment = CourseEnrollmentFactory(course_id=self.course.id, mode=CourseMode.AUDIT)
|
||||
self.assertEqual(enrollment.upgrade_deadline, course_mode.expiration_datetime)
|
||||
|
||||
# The schedule's upgrade deadline should be used if a schedule exists
|
||||
schedule = ScheduleFactory(enrollment=enrollment)
|
||||
self.assertEqual(enrollment.upgrade_deadline, schedule.upgrade_deadline)
|
||||
|
||||
@skip_unless_lms
|
||||
@ddt.data(*(set(CourseMode.ALL_MODES) - set(CourseMode.AUDIT_MODES)))
|
||||
def test_upgrade_deadline_for_non_upgradeable_enrollment(self, mode):
|
||||
""" The property should return None if an upgrade cannot be upgraded. """
|
||||
enrollment = CourseEnrollmentFactory(course_id=self.course.id, mode=mode)
|
||||
self.assertIsNone(enrollment.upgrade_deadline)
|
||||
|
||||
Reference in New Issue
Block a user