Commit bc7f5694 by chrisndodge

Merge pull request #232 from edx/PHX-213

PHX-213 / remove the "expire" status in exam attempt
parents ab676cbc ce852fcd
...@@ -375,21 +375,6 @@ def _create_and_decline_attempt(exam_id, user_id): ...@@ -375,21 +375,6 @@ def _create_and_decline_attempt(exam_id, user_id):
) )
def _create_and_expire_attempt(exam_id, user_id):
"""
It will create the exam attempt and change the attempt's status to decline.
it will auto-decline further exams too
"""
create_exam_attempt(exam_id, user_id)
update_attempt_status(
exam_id,
user_id,
ProctoredExamStudentAttemptStatus.expired,
raise_if_not_found=False
)
def create_exam_attempt(exam_id, user_id, taking_as_proctored=False): def create_exam_attempt(exam_id, user_id, taking_as_proctored=False):
""" """
Creates an exam attempt for user_id against exam_id. There should only be Creates an exam attempt for user_id against exam_id. There should only be
...@@ -498,13 +483,6 @@ def create_exam_attempt(exam_id, user_id, taking_as_proctored=False): ...@@ -498,13 +483,6 @@ def create_exam_attempt(exam_id, user_id, taking_as_proctored=False):
review_policy_id=review_policy.id if review_policy else None, review_policy_id=review_policy.id if review_policy else None,
) )
if is_exam_past_due_date:
update_attempt_status(
exam_id,
user_id,
ProctoredExamStudentAttemptStatus.expired
)
log_msg = ( log_msg = (
'Created exam attempt ({attempt_id}) for exam_id {exam_id} for ' 'Created exam attempt ({attempt_id}) for exam_id {exam_id} for '
'user_id {user_id} with taking as proctored = {taking_as_proctored} ' 'user_id {user_id} with taking as proctored = {taking_as_proctored} '
...@@ -1184,11 +1162,6 @@ STATUS_SUMMARY_MAP = { ...@@ -1184,11 +1162,6 @@ STATUS_SUMMARY_MAP = {
'short_description': _('Failed Proctoring'), 'short_description': _('Failed Proctoring'),
'suggested_icon': 'fa-exclamation-triangle', 'suggested_icon': 'fa-exclamation-triangle',
'in_completed_state': True 'in_completed_state': True
},
ProctoredExamStudentAttemptStatus.expired: {
'short_description': _('Exam Expired'),
'suggested_icon': 'fa-exclamation-triangle',
'in_completed_state': True
} }
} }
...@@ -1208,11 +1181,6 @@ PRACTICE_STATUS_SUMMARY_MAP = { ...@@ -1208,11 +1181,6 @@ PRACTICE_STATUS_SUMMARY_MAP = {
'short_description': _('Practice Exam Failed'), 'short_description': _('Practice Exam Failed'),
'suggested_icon': 'fa-exclamation-triangle', 'suggested_icon': 'fa-exclamation-triangle',
'in_completed_state': True 'in_completed_state': True
},
ProctoredExamStudentAttemptStatus.expired: {
'short_description': _('Exam Expired'),
'suggested_icon': 'fa-exclamation-triangle',
'in_completed_state': True
} }
} }
...@@ -1309,15 +1277,12 @@ def _get_timed_exam_view(exam, context, exam_id, user_id, course_id): ...@@ -1309,15 +1277,12 @@ def _get_timed_exam_view(exam, context, exam_id, user_id, course_id):
has_due_date = True if exam['due_date'] is not None else False has_due_date = True if exam['due_date'] is not None else False
if not attempt_status: if not attempt_status:
if _has_due_date_passed(exam['due_date']): if _has_due_date_passed(exam['due_date']):
_create_and_expire_attempt(exam_id, user_id) student_view_template = 'timed_exam/expired.html'
student_view_template = 'proctored_exam/expired.html'
else: else:
student_view_template = 'timed_exam/entrance.html' student_view_template = 'timed_exam/entrance.html'
elif attempt_status == ProctoredExamStudentAttemptStatus.started: elif attempt_status == ProctoredExamStudentAttemptStatus.started:
# when we're taking the exam we should not override the view # when we're taking the exam we should not override the view
return None return None
elif attempt_status == ProctoredExamStudentAttemptStatus.expired:
student_view_template = 'proctored_exam/expired.html'
elif attempt_status == ProctoredExamStudentAttemptStatus.ready_to_submit: elif attempt_status == ProctoredExamStudentAttemptStatus.ready_to_submit:
student_view_template = 'timed_exam/ready_to_submit.html' student_view_template = 'timed_exam/ready_to_submit.html'
elif attempt_status == ProctoredExamStudentAttemptStatus.submitted: elif attempt_status == ProctoredExamStudentAttemptStatus.submitted:
...@@ -1450,15 +1415,12 @@ def _get_practice_exam_view(exam, context, exam_id, user_id, course_id): ...@@ -1450,15 +1415,12 @@ def _get_practice_exam_view(exam, context, exam_id, user_id, course_id):
if not attempt_status: if not attempt_status:
if _has_due_date_passed(exam['due_date']): if _has_due_date_passed(exam['due_date']):
_create_and_expire_attempt(exam_id, user_id)
student_view_template = 'proctored_exam/expired.html' student_view_template = 'proctored_exam/expired.html'
else: else:
student_view_template = 'practice_exam/entrance.html' student_view_template = 'practice_exam/entrance.html'
elif attempt_status == ProctoredExamStudentAttemptStatus.started: elif attempt_status == ProctoredExamStudentAttemptStatus.started:
# when we're taking the exam we should not override the view # when we're taking the exam we should not override the view
return None return None
elif attempt_status == ProctoredExamStudentAttemptStatus.expired:
student_view_template = 'proctored_exam/expired.html'
elif attempt_status == ProctoredExamStudentAttemptStatus.created: elif attempt_status == ProctoredExamStudentAttemptStatus.created:
provider = get_backend_provider() provider = get_backend_provider()
student_view_template = 'proctored_exam/instructions.html' student_view_template = 'proctored_exam/instructions.html'
...@@ -1545,7 +1507,6 @@ def _get_proctored_exam_view(exam, context, exam_id, user_id, course_id): ...@@ -1545,7 +1507,6 @@ def _get_proctored_exam_view(exam, context, exam_id, user_id, course_id):
# if exam due date has passed, then we can't take the exam # if exam due date has passed, then we can't take the exam
if _has_due_date_passed(exam['due_date']): if _has_due_date_passed(exam['due_date']):
_create_and_expire_attempt(exam_id, user_id)
student_view_template = 'proctored_exam/expired.html' student_view_template = 'proctored_exam/expired.html'
elif not prerequisite_status['are_prerequisites_satisifed']: elif not prerequisite_status['are_prerequisites_satisifed']:
# do we have any declined prerequisites, if so, then we # do we have any declined prerequisites, if so, then we
...@@ -1580,8 +1541,6 @@ def _get_proctored_exam_view(exam, context, exam_id, user_id, course_id): ...@@ -1580,8 +1541,6 @@ def _get_proctored_exam_view(exam, context, exam_id, user_id, course_id):
elif attempt_status == ProctoredExamStudentAttemptStatus.started: elif attempt_status == ProctoredExamStudentAttemptStatus.started:
# when we're taking the exam we should not override the view # when we're taking the exam we should not override the view
return None return None
elif attempt_status == ProctoredExamStudentAttemptStatus.expired:
student_view_template = 'proctored_exam/expired.html'
elif attempt_status in [ProctoredExamStudentAttemptStatus.created, elif attempt_status in [ProctoredExamStudentAttemptStatus.created,
ProctoredExamStudentAttemptStatus.download_software_clicked]: ProctoredExamStudentAttemptStatus.download_software_clicked]:
provider = get_backend_provider() provider = get_backend_provider()
......
...@@ -162,9 +162,6 @@ class ProctoredExamStudentAttemptStatus(object): ...@@ -162,9 +162,6 @@ class ProctoredExamStudentAttemptStatus(object):
# the exam is believed to be in error # the exam is believed to be in error
error = 'error' error = 'error'
# the exam has expired, i.e. past due date
expired = 'expired'
# status alias for sending email # status alias for sending email
status_alias_mapping = { status_alias_mapping = {
submitted: _('pending'), submitted: _('pending'),
...@@ -180,7 +177,7 @@ class ProctoredExamStudentAttemptStatus(object): ...@@ -180,7 +177,7 @@ class ProctoredExamStudentAttemptStatus(object):
""" """
return status in [ return status in [
cls.declined, cls.timed_out, cls.submitted, cls.verified, cls.rejected, cls.declined, cls.timed_out, cls.submitted, cls.verified, cls.rejected,
cls.not_reviewed, cls.error, cls.expired cls.not_reviewed, cls.error
] ]
@classmethod @classmethod
...@@ -200,7 +197,7 @@ class ProctoredExamStudentAttemptStatus(object): ...@@ -200,7 +197,7 @@ class ProctoredExamStudentAttemptStatus(object):
""" """
return to_status in [ return to_status in [
cls.verified, cls.rejected, cls.declined, cls.not_reviewed, cls.submitted, cls.verified, cls.rejected, cls.declined, cls.not_reviewed, cls.submitted,
cls.error, cls.expired cls.error
] ]
@classmethod @classmethod
......
{% load i18n %}
<div class="sequence proctored-exam completed" data-exam-id="{{exam_id}}">
<h3>
{% blocktrans %}
The due date for this exam has passed
{% endblocktrans %}
</h3>
<p>
{% blocktrans %}
Because the due date has passed, you are no longer able to take this exam.
{% endblocktrans %}
</p>
</div>
...@@ -518,25 +518,6 @@ class ProctoredExamApiTests(LoggedInTestCase): ...@@ -518,25 +518,6 @@ class ProctoredExamApiTests(LoggedInTestCase):
minutes_before_past_due_date - 1 <= attempt['allowed_time_limit_mins'] <= minutes_before_past_due_date minutes_before_past_due_date - 1 <= attempt['allowed_time_limit_mins'] <= minutes_before_past_due_date
) )
def test_create_exam_attempt_with_past_due_datetime(self):
"""
Create the exam attempt with past due date
"""
due_date = datetime.now(pytz.UTC) + timedelta(days=1)
# exam is created with due datetime which has already passed
exam_id = self._create_exam_with_due_time(due_date=due_date)
# due_date is exactly after 24 hours, if student arrives after 2 days
# then he can not attempt the proctored exam
reset_time = due_date + timedelta(days=2)
with freeze_time(reset_time):
attempt_id = create_exam_attempt(exam_id, self.user_id)
attempt = get_exam_attempt_by_id(attempt_id)
self.assertEqual(attempt['status'], ProctoredExamStudentAttemptStatus.expired)
def test_create_an_exam_attempt(self): def test_create_an_exam_attempt(self):
""" """
Create an unstarted exam attempt. Create an unstarted exam attempt.
...@@ -681,7 +662,6 @@ class ProctoredExamApiTests(LoggedInTestCase): ...@@ -681,7 +662,6 @@ class ProctoredExamApiTests(LoggedInTestCase):
(ProctoredExamStudentAttemptStatus.submitted, 'submitted'), (ProctoredExamStudentAttemptStatus.submitted, 'submitted'),
(ProctoredExamStudentAttemptStatus.declined, 'declined'), (ProctoredExamStudentAttemptStatus.declined, 'declined'),
(ProctoredExamStudentAttemptStatus.error, 'failed'), (ProctoredExamStudentAttemptStatus.error, 'failed'),
(ProctoredExamStudentAttemptStatus.expired, 'failed'),
) )
@ddt.unpack @ddt.unpack
def test_remove_exam_attempt_with_status(self, to_status, requirement_status): def test_remove_exam_attempt_with_status(self, to_status, requirement_status):
...@@ -1764,12 +1744,6 @@ class ProctoredExamApiTests(LoggedInTestCase): ...@@ -1764,12 +1744,6 @@ class ProctoredExamApiTests(LoggedInTestCase):
None None
), ),
( (
ProctoredExamStudentAttemptStatus.expired,
False,
None,
None
),
(
ProctoredExamStudentAttemptStatus.rejected, ProctoredExamStudentAttemptStatus.rejected,
True, True,
ProctoredExamStudentAttemptStatus.created, ProctoredExamStudentAttemptStatus.created,
...@@ -1866,9 +1840,7 @@ class ProctoredExamApiTests(LoggedInTestCase): ...@@ -1866,9 +1840,7 @@ class ProctoredExamApiTests(LoggedInTestCase):
@ddt.data( @ddt.data(
(ProctoredExamStudentAttemptStatus.declined, ProctoredExamStudentAttemptStatus.eligible), (ProctoredExamStudentAttemptStatus.declined, ProctoredExamStudentAttemptStatus.eligible),
(ProctoredExamStudentAttemptStatus.timed_out, ProctoredExamStudentAttemptStatus.created), (ProctoredExamStudentAttemptStatus.timed_out, ProctoredExamStudentAttemptStatus.created),
(ProctoredExamStudentAttemptStatus.expired, ProctoredExamStudentAttemptStatus.created),
(ProctoredExamStudentAttemptStatus.timed_out, ProctoredExamStudentAttemptStatus.download_software_clicked), (ProctoredExamStudentAttemptStatus.timed_out, ProctoredExamStudentAttemptStatus.download_software_clicked),
(ProctoredExamStudentAttemptStatus.expired, ProctoredExamStudentAttemptStatus.download_software_clicked),
(ProctoredExamStudentAttemptStatus.submitted, ProctoredExamStudentAttemptStatus.ready_to_start), (ProctoredExamStudentAttemptStatus.submitted, ProctoredExamStudentAttemptStatus.ready_to_start),
(ProctoredExamStudentAttemptStatus.verified, ProctoredExamStudentAttemptStatus.started), (ProctoredExamStudentAttemptStatus.verified, ProctoredExamStudentAttemptStatus.started),
(ProctoredExamStudentAttemptStatus.rejected, ProctoredExamStudentAttemptStatus.started), (ProctoredExamStudentAttemptStatus.rejected, ProctoredExamStudentAttemptStatus.started),
...@@ -2071,14 +2043,6 @@ class ProctoredExamApiTests(LoggedInTestCase): ...@@ -2071,14 +2043,6 @@ class ProctoredExamApiTests(LoggedInTestCase):
'suggested_icon': 'fa-exclamation-triangle', 'suggested_icon': 'fa-exclamation-triangle',
'in_completed_state': True 'in_completed_state': True
} }
),
(
ProctoredExamStudentAttemptStatus.expired, {
'status': ProctoredExamStudentAttemptStatus.expired,
'short_description': 'Exam Expired',
'suggested_icon': 'fa-exclamation-triangle',
'in_completed_state': True
}
) )
) )
@ddt.unpack @ddt.unpack
...@@ -2150,14 +2114,6 @@ class ProctoredExamApiTests(LoggedInTestCase): ...@@ -2150,14 +2114,6 @@ class ProctoredExamApiTests(LoggedInTestCase):
'suggested_icon': 'fa-exclamation-triangle', 'suggested_icon': 'fa-exclamation-triangle',
'in_completed_state': True 'in_completed_state': True
} }
),
(
ProctoredExamStudentAttemptStatus.expired, {
'status': ProctoredExamStudentAttemptStatus.expired,
'short_description': 'Exam Expired',
'suggested_icon': 'fa-exclamation-triangle',
'in_completed_state': True
}
) )
) )
@ddt.unpack @ddt.unpack
...@@ -2335,8 +2291,7 @@ class ProctoredExamApiTests(LoggedInTestCase): ...@@ -2335,8 +2291,7 @@ class ProctoredExamApiTests(LoggedInTestCase):
ProctoredExamStudentAttemptStatus.declined, ProctoredExamStudentAttemptStatus.declined,
ProctoredExamStudentAttemptStatus.timed_out, ProctoredExamStudentAttemptStatus.timed_out,
ProctoredExamStudentAttemptStatus.not_reviewed, ProctoredExamStudentAttemptStatus.not_reviewed,
ProctoredExamStudentAttemptStatus.error, ProctoredExamStudentAttemptStatus.error
ProctoredExamStudentAttemptStatus.expired
) )
@patch.dict('settings.PROCTORING_SETTINGS', {'ALLOW_TIMED_OUT_STATE': True}) @patch.dict('settings.PROCTORING_SETTINGS', {'ALLOW_TIMED_OUT_STATE': True})
def test_not_send_email(self, status): def test_not_send_email(self, status):
......
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