Commit 13f398fe by Douglas Hall

Merge pull request #290 from…

Merge pull request #290 from edx/afzaledx/sol-1802_zendesk_tickets_for_suspicious_proctored_attempts

(WIP) SOL-1802 Create Zendesk ticket when "Suspicious" proctored exam reviews are received from the proctoring provider
parents f5df57f8 931ee10c
...@@ -24,6 +24,7 @@ from edx_proctoring.exceptions import ( ...@@ -24,6 +24,7 @@ from edx_proctoring.exceptions import (
ProctoredExamReviewAlreadyExists, ProctoredExamReviewAlreadyExists,
ProctoredExamBadReviewStatus, ProctoredExamBadReviewStatus,
) )
from edx_proctoring.runtime import get_runtime_service
from edx_proctoring.utils import locate_attempt_by_attempt_code, emit_event from edx_proctoring.utils import locate_attempt_by_attempt_code, emit_event
from edx_proctoring. models import ( from edx_proctoring. models import (
ProctoredExamSoftwareSecureReview, ProctoredExamSoftwareSecureReview,
...@@ -64,6 +65,7 @@ class SoftwareSecureBackendProvider(ProctoringBackendProvider): ...@@ -64,6 +65,7 @@ class SoftwareSecureBackendProvider(ProctoringBackendProvider):
self.send_email = send_email self.send_email = send_email
self.passing_review_status = ['Clean', 'Rules Violation'] self.passing_review_status = ['Clean', 'Rules Violation']
self.failing_review_status = ['Not Reviewed', 'Suspicious'] self.failing_review_status = ['Not Reviewed', 'Suspicious']
self.notify_support_for_status = ['Suspicious']
def register_exam_attempt(self, exam, context): def register_exam_attempt(self, exam, context):
""" """
...@@ -261,6 +263,8 @@ class SoftwareSecureBackendProvider(ProctoringBackendProvider): ...@@ -261,6 +263,8 @@ class SoftwareSecureBackendProvider(ProctoringBackendProvider):
exam = serialized_exam_object.data exam = serialized_exam_object.data
emit_event(exam, 'review_received', attempt=attempt, override_data=data) emit_event(exam, 'review_received', attempt=attempt, override_data=data)
self._create_zendesk_ticket(review, serialized_exam_object, serialized_attempt_obj)
def on_review_saved(self, review, allow_rejects=False): # pylint: disable=arguments-differ def on_review_saved(self, review, allow_rejects=False): # pylint: disable=arguments-differ
""" """
called when a review has been save - either through API (on_review_callback) or via Django Admin panel called when a review has been save - either through API (on_review_callback) or via Django Admin panel
...@@ -349,6 +353,20 @@ class SoftwareSecureBackendProvider(ProctoringBackendProvider): ...@@ -349,6 +353,20 @@ class SoftwareSecureBackendProvider(ProctoringBackendProvider):
return (first_name, last_name) return (first_name, last_name)
def _create_zendesk_ticket(self, review, serialized_exam_object, serialized_attempt_obj):
"""
Creates a Zendesk ticket for reviews with status listed in self.notify_support_for_status
"""
if review.review_status in self.notify_support_for_status:
instructor_service = get_runtime_service('instructor')
if instructor_service:
instructor_service.send_support_notification(
course_id=serialized_exam_object["course_id"],
exam_name=serialized_exam_object["exam_name"],
student_username=serialized_attempt_obj["user"]["username"],
review_status=review.review_status
)
def _get_payload(self, exam, context): def _get_payload(self, exam, context):
""" """
Constructs the data payload that Software Secure expects Constructs the data payload that Software Secure expects
......
...@@ -45,7 +45,7 @@ from edx_proctoring.backends.tests.test_review_payload import ( ...@@ -45,7 +45,7 @@ from edx_proctoring.backends.tests.test_review_payload import (
TEST_REVIEW_PAYLOAD TEST_REVIEW_PAYLOAD
) )
from edx_proctoring.tests.test_services import MockCreditService from edx_proctoring.tests.test_services import MockCreditService, MockInstructorService
from edx_proctoring.backends.software_secure import SOFTWARE_SECURE_INVALID_CHARS from edx_proctoring.backends.software_secure import SOFTWARE_SECURE_INVALID_CHARS
...@@ -104,6 +104,7 @@ class SoftwareSecureTests(TestCase): ...@@ -104,6 +104,7 @@ class SoftwareSecureTests(TestCase):
self.user.save() self.user.save()
set_runtime_service('credit', MockCreditService()) set_runtime_service('credit', MockCreditService())
set_runtime_service('instructor', MockInstructorService())
def tearDown(self): def tearDown(self):
""" """
......
...@@ -128,6 +128,12 @@ class MockInstructorService(object): ...@@ -128,6 +128,12 @@ class MockInstructorService(object):
""" """
return self.is_user_course_staff return self.is_user_course_staff
def send_support_notification(self, course_id, exam_name, student_username, review_status):
"""
Mocked implementation of send_support_notification
"""
pass
class TestProctoringService(unittest.TestCase): class TestProctoringService(unittest.TestCase):
""" """
......
...@@ -34,7 +34,7 @@ def load_requirements(*requirements_paths): ...@@ -34,7 +34,7 @@ def load_requirements(*requirements_paths):
setup( setup(
name='edx-proctoring', name='edx-proctoring',
version='0.12.17', version='0.12.18',
description='Proctoring subsystem for Open edX', description='Proctoring subsystem for Open edX',
long_description=open('README.md').read(), long_description=open('README.md').read(),
author='edX', author='edX',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment