From eadd656df04cf94acda4960a38569b992a75da69 Mon Sep 17 00:00:00 2001 From: Afzal Wali Date: Fri, 20 May 2016 15:42:50 +0500 Subject: [PATCH] SOL-1802 Allow proctoring service to create support ticket. --- cms/djangoapps/contentstore/views/item.py | 1 + common/lib/xmodule/xmodule/course_module.py | 9 +++++ .../pages/studio/settings_advanced.py | 3 +- lms/djangoapps/instructor/services.py | 33 +++++++++++++++++++ .../instructor/tests/test_services.py | 29 ++++++++++++++-- requirements/edx/github.txt | 2 +- 6 files changed, 73 insertions(+), 4 deletions(-) diff --git a/cms/djangoapps/contentstore/views/item.py b/cms/djangoapps/contentstore/views/item.py index a47837fe2c..eb40ebed51 100644 --- a/cms/djangoapps/contentstore/views/item.py +++ b/cms/djangoapps/contentstore/views/item.py @@ -987,6 +987,7 @@ def create_xblock_info(xblock, data=None, metadata=None, include_ancestor_info=F if xblock.category == 'course': xblock_info.update({ "enable_proctored_exams": xblock.enable_proctored_exams, + "create_zendesk_tickets": xblock.create_zendesk_tickets, "enable_timed_exams": xblock.enable_timed_exams }) elif xblock.category == 'sequential': diff --git a/common/lib/xmodule/xmodule/course_module.py b/common/lib/xmodule/xmodule/course_module.py index 136d2f963b..361523dcfd 100644 --- a/common/lib/xmodule/xmodule/course_module.py +++ b/common/lib/xmodule/xmodule/course_module.py @@ -742,6 +742,15 @@ class CourseFields(object): scope=Scope.settings ) + create_zendesk_tickets = Boolean( + display_name=_("Create Zendesk Tickets For Suspicious Proctored Exam Attempts"), + help=_( + "Enter true or false. If this value is true, a Zendesk ticket will be created for suspicious attempts." + ), + default=True, + scope=Scope.settings + ) + enable_timed_exams = Boolean( display_name=_("Enable Timed Exams"), help=_( diff --git a/common/test/acceptance/pages/studio/settings_advanced.py b/common/test/acceptance/pages/studio/settings_advanced.py index 87acaf1785..27a7456ff8 100644 --- a/common/test/acceptance/pages/studio/settings_advanced.py +++ b/common/test/acceptance/pages/studio/settings_advanced.py @@ -220,5 +220,6 @@ class AdvancedSettingsPage(CoursePage): 'enable_timed_exams', 'enable_subsection_gating', 'learning_info', - 'instructor_info' + 'instructor_info', + 'create_zendesk_tickets' ] diff --git a/lms/djangoapps/instructor/services.py b/lms/djangoapps/instructor/services.py index 1157e5a653..db5ad000e3 100644 --- a/lms/djangoapps/instructor/services.py +++ b/lms/djangoapps/instructor/services.py @@ -6,10 +6,15 @@ import logging from opaque_keys import InvalidKeyError from opaque_keys.edx.keys import CourseKey, UsageKey +from commerce.signals import create_zendesk_ticket from courseware.models import StudentModule from instructor.views.tools import get_student_from_identifier from django.core.exceptions import ObjectDoesNotExist import instructor.enrollment as enrollment +from django.utils.translation import ugettext as _ + + +from xmodule.modulestore.django import modulestore from student.roles import CourseStaffRole @@ -86,3 +91,31 @@ class InstructorService(object): else Returns False """ return auth.user_has_role(user, CourseStaffRole(CourseKey.from_string(course_id))) + + def send_support_notification(self, course_id, exam_name, student_username, review_status): + """ + Creates a Zendesk ticket for an exam attempt review from the proctoring system. + Currently, it sends notifications for 'Suspicious" status, but additional statuses can be supported + by adding to the notify_support_for_status list in edx_proctoring/backends/software_secure.py + The notifications can be disabled by disabling the + "Create Zendesk Tickets For Suspicious Proctored Exam Attempts" setting in the course's Advanced settings. + """ + + course_key = CourseKey.from_string(course_id) + course = modulestore().get_course(course_key) + + if course.create_zendesk_tickets: + requester_name = "edx-proctoring" + email = "edx-proctoring@edx.org" + subject = _("Proctored Exam Review: {review_status}").format(review_status=review_status) + body = _( + "A proctored exam attempt for {exam_name} in {course_name} by username: {student_username} " + "was reviewed as {review_status} by the proctored exam review provider." + ).format( + exam_name=exam_name, + course_name=course.display_name, + student_username=student_username, + review_status=review_status + ) + tags = ["proctoring"] + create_zendesk_ticket(requester_name, email, subject, body, tags) diff --git a/lms/djangoapps/instructor/tests/test_services.py b/lms/djangoapps/instructor/tests/test_services.py index d05da4574a..f9780d92b3 100644 --- a/lms/djangoapps/instructor/tests/test_services.py +++ b/lms/djangoapps/instructor/tests/test_services.py @@ -10,9 +10,9 @@ from instructor.access import allow_access from instructor.services import InstructorService from instructor.tests.test_tools import msk_from_problem_urlname from nose.plugins.attrib import attr - from student.models import CourseEnrollment from student.tests.factories import UserFactory +import mock @attr('shard_1') @@ -122,7 +122,7 @@ class InstructorServiceTests(SharedModuleStoreTestCase): def test_is_user_staff(self): """ - Test to assert that the usrr is staff or not + Test to assert that the user is staff or not """ result = self.service.is_course_staff( self.student, @@ -137,3 +137,28 @@ class InstructorServiceTests(SharedModuleStoreTestCase): unicode(self.course.id) ) self.assertTrue(result) + + def test_report_suspicious_attempt(self): + """ + Test to verify that the create_zendesk_ticket() is called + """ + requester_name = "edx-proctoring" + email = "edx-proctoring@edx.org" + subject = "Proctored Exam Review: {review_status}".format(review_status="Suspicious") + body = "A proctored exam attempt for {exam_name} in {course_name} by username: {student_username} was " \ + "reviewed as {review_status} by the proctored exam review provider." + body = body.format( + exam_name="test_exam", course_name=self.course.display_name, student_username="test_student", + review_status="Suspicious" + ) + tags = ["proctoring"] + + with mock.patch("instructor.services.create_zendesk_ticket") as mock_create_zendesk_ticket: + self.service.send_support_notification( + course_id=unicode(self.course.id), + exam_name="test_exam", + student_username="test_student", + review_status="Suspicious" + ) + + mock_create_zendesk_ticket.assert_called_with(requester_name, email, subject, body, tags) diff --git a/requirements/edx/github.txt b/requirements/edx/github.txt index 1af2f4cb61..7334814034 100644 --- a/requirements/edx/github.txt +++ b/requirements/edx/github.txt @@ -90,7 +90,7 @@ git+https://github.com/edx/xblock-utils.git@v1.0.2#egg=xblock-utils==1.0.2 -e git+https://github.com/edx/edx-reverification-block.git@0.0.5#egg=edx-reverification-block==0.0.5 git+https://github.com/edx/edx-user-state-client.git@1.0.1#egg=edx-user-state-client==1.0.1 git+https://github.com/edx/xblock-lti-consumer.git@v1.0.6#egg=xblock-lti-consumer==1.0.6 -git+https://github.com/edx/edx-proctoring.git@0.12.17#egg=edx-proctoring==0.12.17 +git+https://github.com/edx/edx-proctoring.git@0.12.18#egg=edx-proctoring==0.12.18 # Third Party XBlocks -e git+https://github.com/mitodl/edx-sga@172a90fd2738f8142c10478356b2d9ed3e55334a#egg=edx-sga