Commit d30a3a91 by chrisndodge

Merge pull request #193 from edx/cdodge/archive-attempt-updates

archive any change to the attempt status
parents 00819219 8ac77ad7
......@@ -803,7 +803,9 @@ class SoftwareSecureTests(TestCase):
# look at the attempt again, since it moved into Archived state
# then it should still remain unchanged
archived_attempt = ProctoredExamStudentAttemptHistory.objects.get(attempt_code=attempt['attempt_code'])
archived_attempt = ProctoredExamStudentAttemptHistory.objects.filter(
attempt_code=attempt['attempt_code']
).latest('created')
self.assertEqual(archived_attempt.status, attempt['status'])
......
......@@ -525,6 +525,9 @@ class ProctoredExamStudentAttemptHistory(TimeStampedModel):
# this ID might point to a record that is in the History table
review_policy_id = models.IntegerField(null=True)
last_poll_timestamp = models.DateTimeField(null=True)
last_poll_ipaddr = models.CharField(max_length=32, null=True)
@classmethod
def get_exam_attempt_by_code(cls, attempt_code):
"""
......@@ -537,9 +540,10 @@ class ProctoredExamStudentAttemptHistory(TimeStampedModel):
# there are any
exam_attempt_obj = None
items = cls.objects.filter(attempt_code=attempt_code).order_by("-created")
if items:
exam_attempt_obj = items[0]
try:
exam_attempt_obj = cls.objects.filter(attempt_code=attempt_code).latest("created")
except cls.DoesNotExist: # pylint: disable=no-member
pass
return exam_attempt_obj
......@@ -570,10 +574,48 @@ def on_attempt_deleted(sender, instance, **kwargs): # pylint: disable=unused-ar
is_sample_attempt=instance.is_sample_attempt,
student_name=instance.student_name,
review_policy_id=instance.review_policy_id,
last_poll_timestamp=instance.last_poll_timestamp,
last_poll_ipaddr=instance.last_poll_ipaddr,
)
archive_object.save()
@receiver(pre_save, sender=ProctoredExamStudentAttempt)
def on_attempt_updated(sender, instance, **kwargs): # pylint: disable=unused-argument
"""
Archive the exam attempt whenever the attempt status is about to be
modified. Make a new entry with the previous value of the status in the
ProctoredExamStudentAttemptHistory table.
"""
if instance.id:
# on an update case, get the original
# and see if the status has changed, if so, then we need
# to archive it
original = ProctoredExamStudentAttempt.objects.get(id=instance.id)
if original.status != instance.status:
archive_object = ProctoredExamStudentAttemptHistory(
user=original.user,
attempt_id=original.id,
proctored_exam=original.proctored_exam,
started_at=original.started_at,
completed_at=original.completed_at,
attempt_code=original.attempt_code,
external_id=original.external_id,
allowed_time_limit_mins=original.allowed_time_limit_mins,
status=original.status,
taking_as_proctored=original.taking_as_proctored,
is_sample_attempt=original.is_sample_attempt,
student_name=original.student_name,
review_policy_id=original.review_policy_id,
last_poll_timestamp=original.last_poll_timestamp,
last_poll_ipaddr=original.last_poll_ipaddr,
)
archive_object.save()
class QuerySetWithUpdateOverride(models.query.QuerySet):
"""
Custom QuerySet class to make an archive copy
......
"""
All tests for the models.py
"""
# pylint: disable=invalid-name
from edx_proctoring.models import (
ProctoredExam,
ProctoredExamStudentAllowance,
......@@ -9,6 +10,7 @@ from edx_proctoring.models import (
ProctoredExamStudentAttemptHistory,
ProctoredExamReviewPolicy,
ProctoredExamReviewPolicyHistory,
ProctoredExamStudentAttemptStatus,
)
from .utils import (
......@@ -174,6 +176,52 @@ class ProctoredExamStudentAttemptTests(LoggedInTestCase):
deleted_item = ProctoredExamStudentAttemptHistory.get_exam_attempt_by_code("123456")
self.assertEqual(deleted_item.student_name, "John. D Updated")
def test_update_proctored_exam_attempt(self):
"""
Deleting the proctored exam attempt creates an entry in the history table.
"""
proctored_exam = ProctoredExam.objects.create(
course_id='test_course',
content_id='test_content',
exam_name='Test Exam',
external_id='123aXqe3',
time_limit_mins=90
)
attempt = ProctoredExamStudentAttempt.objects.create(
proctored_exam_id=proctored_exam.id,
user_id=1,
status=ProctoredExamStudentAttemptStatus.created,
student_name="John. D",
allowed_time_limit_mins=10,
attempt_code="123456",
taking_as_proctored=True,
is_sample_attempt=True,
external_id=1
)
# No entry in the History table on creation of the Allowance entry.
attempt_history = ProctoredExamStudentAttemptHistory.objects.filter(user_id=1)
self.assertEqual(len(attempt_history), 0)
# re-saving, but not changing status should not make an archive copy
attempt.student_name = 'John. D Updated'
attempt.save()
attempt_history = ProctoredExamStudentAttemptHistory.objects.filter(user_id=1)
self.assertEqual(len(attempt_history), 0)
# change status...
attempt.status = ProctoredExamStudentAttemptStatus.started
attempt.save()
attempt_history = ProctoredExamStudentAttemptHistory.objects.filter(user_id=1)
self.assertEqual(len(attempt_history), 1)
# make sure we can ready it back with helper class method
updated_item = ProctoredExamStudentAttemptHistory.get_exam_attempt_by_code("123456")
self.assertEqual(updated_item.student_name, "John. D Updated")
self.assertEqual(updated_item.status, ProctoredExamStudentAttemptStatus.created)
def test_get_exam_attempts(self):
"""
Test to get all the exam attempts for a course
......
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