From c95dc593566d184971f63cd4e2da0f6f25f1f879 Mon Sep 17 00:00:00 2001 From: Bianca Severino Date: Thu, 14 Jan 2021 17:04:31 -0500 Subject: [PATCH] Add function to InstructorService to retrieve proctoring_escalation_email --- lms/djangoapps/instructor/services.py | 19 +++++++++++++ .../instructor/tests/test_services.py | 27 ++++++++++++++++++- requirements/edx/base.txt | 2 +- requirements/edx/development.txt | 2 +- requirements/edx/testing.txt | 2 +- 5 files changed, 48 insertions(+), 4 deletions(-) diff --git a/lms/djangoapps/instructor/services.py b/lms/djangoapps/instructor/services.py index 22e19aff6a..6d38ac6cf8 100644 --- a/lms/djangoapps/instructor/services.py +++ b/lms/djangoapps/instructor/services.py @@ -9,6 +9,7 @@ from django.core.exceptions import ObjectDoesNotExist from django.utils.translation import ugettext as _ from opaque_keys import InvalidKeyError from opaque_keys.edx.keys import CourseKey, UsageKey +from opaque_keys.edx.locator import CourseLocator import lms.djangoapps.instructor.enrollment as enrollment from lms.djangoapps.courseware.models import StudentModule @@ -27,6 +28,9 @@ class InstructorService(object): for the edx_proctoring's dependency injection to cater for a requirement where edx_proctoring needs to call into edx-platform's functions to delete the students' existing answers, grades and attempt counts if there had been an earlier attempt. + + This service also contains utility functions to check if a user is course staff, send notifications + related to proctored exam attempts, and retrieve a course team's proctoring escalation email. """ def delete_student_attempt(self, student_identifier, course_id, content_id, requesting_user): @@ -118,3 +122,18 @@ class InstructorService(object): ) tags = ["proctoring"] create_zendesk_ticket(requester_name, email, subject, body, tags) + + def get_proctoring_escalation_email(self, course_key): + """ + Returns the proctoring escalation email for a course, or None if not given. + + Example arguments: + * course_key (String): 'block-v1:edX+DemoX+Demo_Course' + """ + # Convert course key into id + course_id = CourseLocator.from_string(course_key) + course = modulestore().get_course(course_id) + if course is None: + raise ObjectDoesNotExist('Course not found for course_key {course_key}.') + + return course.proctoring_escalation_email diff --git a/lms/djangoapps/instructor/tests/test_services.py b/lms/djangoapps/instructor/tests/test_services.py index c83b81c0c5..67e57b49ec 100644 --- a/lms/djangoapps/instructor/tests/test_services.py +++ b/lms/djangoapps/instructor/tests/test_services.py @@ -7,6 +7,9 @@ import json import mock import six +from opaque_keys import InvalidKeyError + +from django.core.exceptions import ObjectDoesNotExist from lms.djangoapps.courseware.models import StudentModule from lms.djangoapps.instructor.access import allow_access @@ -26,7 +29,8 @@ class InstructorServiceTests(SharedModuleStoreTestCase): @classmethod def setUpClass(cls): super(InstructorServiceTests, cls).setUpClass() - cls.course = CourseFactory.create() + cls.email = 'escalation@test.com' + cls.course = CourseFactory.create(proctoring_escalation_email=cls.email) cls.problem_location = msk_from_problem_urlname( cls.course.id, 'robot-some-problem-urlname' @@ -185,3 +189,24 @@ class InstructorServiceTests(SharedModuleStoreTestCase): ) expected_body = body.format(**args) mock_create_zendesk_ticket.assert_called_with(requester_name, email, subject, expected_body, tags) + + def test_get_proctoring_escalation_email(self): + """ + Test that it returns the correct proctoring escalation email + """ + email = self.service.get_proctoring_escalation_email(str(self.course.id)) + self.assertEqual(email, self.email) + + def test_get_proctoring_escalation_email_no_course(self): + """ + Test that it raises an exception if the course is not found + """ + with self.assertRaises(ObjectDoesNotExist): + self.service.get_proctoring_escalation_email('a/b/c') + + def test_get_proctoring_escalation_email_invalid_key(self): + """ + Test that it raises an exception if the course_key is invalid + """ + with self.assertRaises(InvalidKeyError): + self.service.get_proctoring_escalation_email('invalid key') diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt index 18104959e5..1c81904bde 100644 --- a/requirements/edx/base.txt +++ b/requirements/edx/base.txt @@ -105,7 +105,7 @@ edx-milestones==0.3.0 # via -r requirements/edx/base.in edx-opaque-keys[django]==2.1.1 # via -r requirements/edx/paver.txt, edx-bulk-grades, edx-ccx-keys, edx-completion, edx-drf-extensions, edx-enterprise, edx-milestones, edx-organizations, edx-proctoring, edx-user-state-client, edx-when, lti-consumer-xblock, xmodule edx-organizations==6.7.1 # via -r requirements/edx/base.in edx-proctoring-proctortrack==1.0.5 # via -r requirements/edx/base.in -edx-proctoring==2.5.13 # via -r requirements/edx/base.in, edx-proctoring-proctortrack +edx-proctoring==2.6.0 # via -r requirements/edx/base.in, edx-proctoring-proctortrack edx-rbac==1.4.1 # via edx-enterprise edx-rest-api-client==5.3.0 # via -r requirements/edx/base.in, edx-enterprise, edx-proctoring edx-search==3.0.0 # via -r requirements/edx/base.in diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt index 7a58fcad13..91b9ecebb9 100644 --- a/requirements/edx/development.txt +++ b/requirements/edx/development.txt @@ -117,7 +117,7 @@ edx-milestones==0.3.0 # via -r requirements/edx/testing.txt edx-opaque-keys[django]==2.1.1 # via -r requirements/edx/testing.txt, edx-bulk-grades, edx-ccx-keys, edx-completion, edx-drf-extensions, edx-enterprise, edx-milestones, edx-organizations, edx-proctoring, edx-user-state-client, edx-when, lti-consumer-xblock, xmodule edx-organizations==6.7.1 # via -r requirements/edx/testing.txt edx-proctoring-proctortrack==1.0.5 # via -r requirements/edx/testing.txt -edx-proctoring==2.5.13 # via -r requirements/edx/testing.txt, edx-proctoring-proctortrack +edx-proctoring==2.6.0 # via -r requirements/edx/testing.txt, edx-proctoring-proctortrack edx-rbac==1.4.1 # via -r requirements/edx/testing.txt, edx-enterprise edx-rest-api-client==5.3.0 # via -r requirements/edx/testing.txt, edx-enterprise, edx-proctoring edx-search==3.0.0 # via -r requirements/edx/testing.txt diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt index 5ee06da97d..4ac5deb7c6 100644 --- a/requirements/edx/testing.txt +++ b/requirements/edx/testing.txt @@ -114,7 +114,7 @@ edx-milestones==0.3.0 # via -r requirements/edx/base.txt edx-opaque-keys[django]==2.1.1 # via -r requirements/edx/base.txt, edx-bulk-grades, edx-ccx-keys, edx-completion, edx-drf-extensions, edx-enterprise, edx-milestones, edx-organizations, edx-proctoring, edx-user-state-client, edx-when, lti-consumer-xblock, xmodule edx-organizations==6.7.1 # via -r requirements/edx/base.txt edx-proctoring-proctortrack==1.0.5 # via -r requirements/edx/base.txt -edx-proctoring==2.5.13 # via -r requirements/edx/base.txt, edx-proctoring-proctortrack +edx-proctoring==2.6.0 # via -r requirements/edx/base.txt, edx-proctoring-proctortrack edx-rbac==1.4.1 # via -r requirements/edx/base.txt, edx-enterprise edx-rest-api-client==5.3.0 # via -r requirements/edx/base.txt, edx-enterprise, edx-proctoring edx-search==3.0.0 # via -r requirements/edx/base.txt