Skip to content

Commit

Permalink
allow for optional (default True) ability to allow SoftwareSecure to …
Browse files Browse the repository at this point in the history
…retransmit proctored results
  • Loading branch information
Chris Dodge committed Sep 23, 2015
1 parent 8d58fc8 commit 97e1650
Show file tree
Hide file tree
Showing 5 changed files with 342 additions and 20 deletions.
42 changes: 28 additions & 14 deletions edx_proctoring/backends/software_secure.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from django.conf import settings

from edx_proctoring.backends.backend import ProctoringBackendProvider
from edx_proctoring import constants
from edx_proctoring.exceptions import (
BackendProvideCannotRegisterAttempt,
StudentExamAttemptDoesNotExistsException,
Expand Down Expand Up @@ -188,29 +189,42 @@ def on_review_callback(self, payload):
)
raise ProctoredExamSuspiciousLookup(err_msg)

# do we already have a review for this attempt?!? It should not be updated!
# do some limited parsing of the JSON payload
review_status = payload['reviewStatus']
video_review_link = payload['videoReviewLink']

# do we already have a review for this attempt?!? We may not allow updates
review = ProctoredExamSoftwareSecureReview.get_review_by_attempt_code(attempt_code)

if review:
err_msg = (
if not constants.ALLOW_REVIEW_UPDATES:
err_msg = (
'We already have a review submitted from SoftwareSecure regarding '
'attempt_code {attempt_code}. We do not allow for updates!'.format(
attempt_code=attempt_code
)
)
raise ProctoredExamReviewAlreadyExists(err_msg)

# we allow updates
warn_msg = (
'We already have a review submitted from SoftwareSecure regarding '
'attempt_code {attempt_code}. We do not allow for updates!'.format(
'attempt_code {attempt_code}. We have been configured to allow for '
'updates and will continue...'.format(
attempt_code=attempt_code
)
)
raise ProctoredExamReviewAlreadyExists(err_msg)
log.warn(warn_msg)
else:
# this is first time we've received this attempt_code, so
# make a new record in the review table
review = ProctoredExamSoftwareSecureReview()

# do some limited parsing of the JSON payload
review_status = payload['reviewStatus']
video_review_link = payload['videoReviewLink']
review.attempt_code = attempt_code
review.raw_data = json.dumps(payload)
review.review_status = review_status
review.video_url = video_review_link

# make a new record in the review table
review = ProctoredExamSoftwareSecureReview(
attempt_code=attempt_code,
raw_data=json.dumps(payload),
review_status=review_status,
video_url=video_review_link,
)
review.save()

# go through and populate all of the specific comments
Expand Down
72 changes: 71 additions & 1 deletion edx_proctoring/backends/tests/test_software_secure.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# coding=utf-8
# pylint: disable=too-many-lines, invalid-name
"""
Tests for the software_secure module
"""
Expand Down Expand Up @@ -32,6 +33,7 @@
ProctoredExamSoftwareSecureReview,
ProctoredExamSoftwareSecureComment,
ProctoredExamStudentAttemptStatus,
ProctoredExamSoftwareSecureReviewHistory,
)
from edx_proctoring.backends.tests.test_review_payload import TEST_REVIEW_PAYLOAD

Expand Down Expand Up @@ -471,7 +473,8 @@ def test_review_on_archived_attempt(self):

self.assertEqual(len(comments), 6)

def test_review_resubmission(self):
@patch('edx_proctoring.constants.ALLOW_REVIEW_UPDATES', False)
def test_disallow_review_resubmission(self):
"""
Tests that an exception is raised if a review report is resubmitted for the same
attempt
Expand Down Expand Up @@ -508,3 +511,70 @@ def test_review_resubmission(self):
# now call again
with self.assertRaises(ProctoredExamReviewAlreadyExists):
provider.on_review_callback(json.loads(test_payload))

@patch('edx_proctoring.constants.ALLOW_REVIEW_UPDATES', True)
def test_allow_review_resubmission(self):
"""
Tests that an resubmission is allowed
"""

provider = get_backend_provider()

exam_id = create_exam(
course_id='foo/bar/baz',
content_id='content',
exam_name='Sample Exam',
time_limit_mins=10,
is_proctored=True
)

# be sure to use the mocked out SoftwareSecure handlers
with HTTMock(mock_response_content):
attempt_id = create_exam_attempt(
exam_id,
self.user.id,
taking_as_proctored=True
)

attempt = get_exam_attempt_by_id(attempt_id)
self.assertIsNotNone(attempt['external_id'])

test_payload = Template(TEST_REVIEW_PAYLOAD).substitute(
attempt_code=attempt['attempt_code'],
external_id=attempt['external_id']
)

provider.on_review_callback(json.loads(test_payload))

# make sure history table is empty
records = ProctoredExamSoftwareSecureReviewHistory.objects.filter(attempt_code=attempt['attempt_code'])
self.assertEqual(len(records), 0)

# now call again, this will not throw exception
test_payload = test_payload.replace('Clean', 'Suspicious')
provider.on_review_callback(json.loads(test_payload))

# make sure that what we have in the Database matches what we expect
review = ProctoredExamSoftwareSecureReview.get_review_by_attempt_code(attempt['attempt_code'])

self.assertIsNotNone(review)
self.assertEqual(review.review_status, 'Suspicious')
self.assertEqual(
review.video_url,
'http://www.remoteproctor.com/AdminSite/Account/Reviewer/DirectLink-Generic.aspx?ID=foo'
)
self.assertIsNotNone(review.raw_data)

# make sure history table is no longer empty
records = ProctoredExamSoftwareSecureReviewHistory.objects.filter(attempt_code=attempt['attempt_code'])
self.assertEqual(len(records), 1)
self.assertEqual(records[0].review_status, 'Clean')

# now try to delete the record and make sure it was archived

review.delete()

records = ProctoredExamSoftwareSecureReviewHistory.objects.filter(attempt_code=attempt['attempt_code'])
self.assertEqual(len(records), 2)
self.assertEqual(records[0].review_status, 'Clean')
self.assertEqual(records[1].review_status, 'Suspicious')
5 changes: 5 additions & 0 deletions edx_proctoring/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,8 @@
settings.PROCTORING_SETTINGS['CONTACT_EMAIL'] if
'CONTACT_EMAIL' in settings.PROCTORING_SETTINGS else getattr(settings, 'CONTACT_EMAIL', FROM_EMAIL)
)

ALLOW_REVIEW_UPDATES = (
settings.PROCTORING_SETTINGS['ALLOW_REVIEW_UPDATES'] if
'ALLOW_REVIEW_UPDATES' in settings.PROCTORING_SETTINGS else getattr(settings, 'ALLOW_REVIEW_UPDATES', True)
)

0 comments on commit 97e1650

Please sign in to comment.