Check for an existing proctored exam before sending proctoring requirements email

This commit is contained in:
Bianca Severino
2021-02-03 14:41:12 -05:00
parent eba1e282da
commit 3f5cbcfc6a
4 changed files with 83 additions and 13 deletions

View File

@@ -9,6 +9,7 @@ from django.conf import settings
from lms.djangoapps.verify_student.services import IDVerificationService
from openedx.core.djangoapps.ace_common.template_context import get_base_template_context
from openedx.core.djangoapps.enrollments.api import is_enrollment_valid_for_proctoring
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
from xmodule.modulestore.django import modulestore
@@ -51,3 +52,26 @@ def generate_proctoring_requirements_email_context(user, course_id):
'proctoring_requirements_url': settings.PROCTORING_SETTINGS.get('LINK_URLS', {}).get('faq', ''),
'id_verification_url': IDVerificationService.get_verify_location(),
}
def should_send_proctoring_requirements_email(username, course_id):
"""
Returns a boolean whether a proctoring requirements email should be sent.
Arguments:
* username (str): The user associated with the enrollment.
* course_id (str): The course id associated with the enrollment.
"""
if not is_enrollment_valid_for_proctoring(username, course_id):
return False
# Only send if a proctored exam is found in the course
timed_exams = modulestore().get_items(
course_id,
qualifiers={'category': 'sequential'},
settings={'is_time_limited': True}
)
has_proctored_exam = any([exam.is_proctored_exam for exam in timed_exams])
return has_proctored_exam

View File

@@ -59,7 +59,10 @@ from user_util import user_util
import openedx.core.djangoapps.django_comment_common.comment_client as cc
from common.djangoapps.course_modes.models import CourseMode, get_cosmetic_verified_display_price
from common.djangoapps.student.emails import send_proctoring_requirements_email
from common.djangoapps.student.email_helpers import generate_proctoring_requirements_email_context
from common.djangoapps.student.email_helpers import (
generate_proctoring_requirements_email_context,
should_send_proctoring_requirements_email
)
from common.djangoapps.student.signals import ENROLL_STATUS_CHANGE, ENROLLMENT_TRACK_UPDATED, UNENROLL_DONE
from common.djangoapps.track import contexts, segment
from common.djangoapps.util.model_utils import emit_field_changed_events, get_changed_fields_dict
@@ -76,7 +79,6 @@ from openedx.core.djangoapps.content.course_overviews.models import CourseOvervi
from openedx.core.djangoapps.enrollments.api import (
_default_course_mode,
get_enrollment_attributes,
is_enrollment_valid_for_proctoring,
set_enrollment_attributes,
)
from openedx.core.djangoapps.signals.signals import USER_ACCOUNT_ACTIVATED
@@ -1461,7 +1463,7 @@ class CourseEnrollment(models.Model):
if mode_changed:
if COURSEWARE_PROCTORING_IMPROVEMENTS.is_enabled(self.course_id):
# If mode changed to one that requires proctoring, send proctoring requirements email
if is_enrollment_valid_for_proctoring(self.user.username, self.course_id):
if should_send_proctoring_requirements_email(self.user.username, self.course_id):
email_context = generate_proctoring_requirements_email_context(self.user, self.course_id)
send_proctoring_requirements_email(context=email_context)

View File

@@ -26,7 +26,7 @@ from common.djangoapps.student.tests.factories import CourseEnrollmentAllowedFac
from common.djangoapps.util.testing import UrlResetMixin
from lms.djangoapps.courseware.toggles import COURSEWARE_PROCTORING_IMPROVEMENTS
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
@ddt.ddt
@@ -48,7 +48,12 @@ class EnrollmentTest(UrlResetMixin, SharedModuleStoreTestCase):
super(EnrollmentTest, cls).setUpClass()
cls.course = CourseFactory.create()
cls.course_limited = CourseFactory.create()
cls.proctored_course = CourseFactory(enable_proctored_exams=True)
cls.proctored_course = CourseFactory(
enable_proctored_exams=True, enable_timed_exams=True
)
cls.proctored_course_no_exam = CourseFactory(
enable_proctored_exams=True, enable_timed_exams=True
)
@patch.dict(settings.FEATURES, {'EMBARGO': True})
def setUp(self):
@@ -61,6 +66,21 @@ class EnrollmentTest(UrlResetMixin, SharedModuleStoreTestCase):
self.urls = [
reverse('course_modes_choose', kwargs={'course_id': six.text_type(self.course.id)})
]
# Set up proctored exam
self._create_proctored_exam(self.proctored_course)
def _create_proctored_exam(self, course):
"""
Helper function to create a proctored exam for a given course
"""
chapter = ItemFactory.create(
parent=course, category='chapter', display_name='Test Section', publish_item=True
)
ItemFactory.create(
parent=chapter, category='sequential', display_name='Test Proctored Exam',
graded=True, is_time_limited=True, default_time_limit_minutes=10,
is_proctored_exam=True, publish_item=True
)
@ddt.data(
# Default (no course modes in the database)
@@ -197,10 +217,24 @@ class EnrollmentTest(UrlResetMixin, SharedModuleStoreTestCase):
CourseEnrollment.enroll(self.user, self.proctored_course.id, mode) # pylint: disable=no-member
self.assertEqual(email_sent, mock_send_email.called)
def test_enroll_in_proctored_course_no_exam(self):
"""
If a verified learner enrolls in a course that has proctoring enabled, but does not have
any proctored exams, they should not receive a proctoring requirements email.
"""
with patch(
'common.djangoapps.student.models.send_proctoring_requirements_email',
return_value=None
) as mock_send_email:
CourseEnrollment.enroll(
self.user, self.proctored_course_no_exam.id, 'verified' # pylint: disable=no-member
)
self.assertFalse(mock_send_email.called)
@ddt.data('verified', 'masters', 'professional', 'executive-education')
def test_upgrade_proctoring_enrollment(self, mode):
"""
When upgrading from audit in a proctoring-enabled course, an email with proctoring requirements
When upgrading from audit in a course with proctored exams, an email with proctoring requirements
should be sent.
"""
with patch(
@@ -219,15 +253,18 @@ class EnrollmentTest(UrlResetMixin, SharedModuleStoreTestCase):
def test_enroll_in_proctored_course_honor_mode_allowed(self):
"""
If the proctoring provider allows honor mode, send proctoring requirements email when learners
enroll in honor mode for a proctoring-enabled course.
enroll in honor mode for a course with proctored exams.
"""
with patch(
'common.djangoapps.student.models.send_proctoring_requirements_email',
return_value=None
) as mock_send_email:
course_honor_mode = CourseFactory(
enable_proctored_exams=True, proctoring_provider='test_provider_honor_mode'
enable_proctored_exams=True,
enable_timed_exams=True,
proctoring_provider='test_provider_honor_mode',
)
self._create_proctored_exam(course_honor_mode)
CourseEnrollment.enroll(self.user, course_honor_mode.id, 'honor') # pylint: disable=no-member
self.assertTrue(mock_send_email.called)