Commit a7389c5d by Chris Dodge

complete providing attempt summary info

parent 2f9b287c
...@@ -7,7 +7,11 @@ API which is in the views.py file, per edX coding standards ...@@ -7,7 +7,11 @@ API which is in the views.py file, per edX coding standards
""" """
import pytz import pytz
import uuid import uuid
import logging
from datetime import datetime, timedelta from datetime import datetime, timedelta
from django.utils.translation import ugettext as _
from django.conf import settings from django.conf import settings
from django.template import Context, loader from django.template import Context, loader
from django.core.urlresolvers import reverse, NoReverseMatch from django.core.urlresolvers import reverse, NoReverseMatch
...@@ -36,6 +40,8 @@ from edx_proctoring.utils import humanized_time ...@@ -36,6 +40,8 @@ from edx_proctoring.utils import humanized_time
from edx_proctoring.backends import get_backend_provider from edx_proctoring.backends import get_backend_provider
from edx_proctoring.runtime import get_runtime_service from edx_proctoring.runtime import get_runtime_service
log = logging.getLogger("edx_proctoring_views")
def is_feature_enabled(): def is_feature_enabled():
""" """
...@@ -646,6 +652,45 @@ def _check_credit_eligibility(credit_state): ...@@ -646,6 +652,45 @@ def _check_credit_eligibility(credit_state):
return True return True
STATUS_SUMMARY_MAP = {
'_default': {
'short_description': _('Taking As Proctored Exam'),
'suggested_icon': 'fa-lock',
'in_completed_state': False
},
ProctoredExamStudentAttemptStatus.eligible: {
'short_description': _('Proctored Option Available'),
'suggested_icon': 'fa-lock',
'in_completed_state': False
},
ProctoredExamStudentAttemptStatus.declined: {
'short_description': _('Taking As Open Exam'),
'suggested_icon': 'fa-unlock',
'in_completed_state': False
},
ProctoredExamStudentAttemptStatus.submitted: {
'short_description': _('Pending Session Review'),
'suggested_icon': 'fa-spinner fa-spin',
'in_completed_state': True
},
ProctoredExamStudentAttemptStatus.verified: {
'short_description': _('Passed Proctoring'),
'suggested_icon': 'fa-check',
'in_completed_state': True
},
ProctoredExamStudentAttemptStatus.rejected: {
'short_description': _('Failed Proctoring'),
'suggested_icon': 'fa-exclamation-triangle',
'in_completed_state': True
},
ProctoredExamStudentAttemptStatus.error: {
'short_description': _('Failed Proctoring'),
'suggested_icon': 'fa-exclamation-triangle',
'in_completed_state': True
}
}
def get_attempt_status_summary(user_id, course_id, content_id): def get_attempt_status_summary(user_id, course_id, content_id):
""" """
Returns a summary about the status of the attempt for the user Returns a summary about the status of the attempt for the user
...@@ -657,7 +702,8 @@ def get_attempt_status_summary(user_id, course_id, content_id): ...@@ -657,7 +702,8 @@ def get_attempt_status_summary(user_id, course_id, content_id):
{ {
'status': ['eligible', 'declined', 'submitted', 'verified', 'rejected'], 'status': ['eligible', 'declined', 'submitted', 'verified', 'rejected'],
'short_description': <short description of status>, 'short_description': <short description of status>,
'suggested-icon': <recommended font-awesome icon to use> 'suggested_icon': <recommended font-awesome icon to use>,
'in_completed_state': <if the status is considered in a 'completed' state>
} }
""" """
...@@ -675,50 +721,18 @@ def get_attempt_status_summary(user_id, course_id, content_id): ...@@ -675,50 +721,18 @@ def get_attempt_status_summary(user_id, course_id, content_id):
log.exception(ex) log.exception(ex)
return None return None
attempt = get_exam_attempt(exam_id, user_id) attempt = get_exam_attempt(exam['id'], user_id)
status = attempt['status'] if attempt else ProctoredExamStudentAttemptStatus.eligible
if not attempt: summary = None
return { if status in STATUS_SUMMARY_MAP:
'status': ProctoredExamStudentAttemptStatus.eligible, summary = STATUS_SUMMARY_MAP[status]
'short_description': _('Proctored Option Available'),
'suggested-icon': 'fa-lock'
}
elif attempt['status'] == ProctoredExamStudentAttemptStatus.declined:
return {
'status': ProctoredExamStudentAttemptStatus.eligible,
'short_description': _('Taking As Open Exam'),
'suggested-icon': 'fa-unlock'
}
elif attempt['status'] == ProctoredExamStudentAttemptStatus.submitted:
return {
'status': ProctoredExamStudentAttemptStatus.submitted,
'short_description': _('Pending Session Review'),
'suggested-icon': 'fa-unlock'
}
elif attempt['status'] == ProctoredExamStudentAttemptStatus.verified:
return {
'status': ProctoredExamStudentAttemptStatus.submitted,
'short_description': _('Passed Proctoring'),
'suggested-icon': 'fa-check'
}
elif attempt['status'] == ProctoredExamStudentAttemptStatus.rejected:
return {
'status': ProctoredExamStudentAttemptStatus.rejected,
'short_description': _('Failed Proctoring'),
'suggested-icon': 'fa-exclamation-triangle'
}
elif attempt['status'] == ProctoredExamStudentAttemptStatus.started:
return {
'status': ProctoredExamStudentAttemptStatus.started,
'short_description': _('Taking As Proctored Exam'),
'suggested-icon': 'fa-lock'
}
else: else:
return { summary = STATUS_SUMMARY_MAP['_default']
'status': ProctoredExamStudentAttemptStatus.eligible,
'short_description': _('Proctored Option Available'), summary.update({"status": status})
'suggested-icon': 'fa-lock'
} return summary
def get_student_view(user_id, course_id, content_id, def get_student_view(user_id, course_id, content_id,
......
...@@ -33,6 +33,7 @@ from edx_proctoring.api import ( ...@@ -33,6 +33,7 @@ from edx_proctoring.api import (
mark_exam_attempt_timeout, mark_exam_attempt_timeout,
mark_exam_attempt_as_ready, mark_exam_attempt_as_ready,
update_attempt_status, update_attempt_status,
get_attempt_status_summary,
) )
from edx_proctoring.exceptions import ( from edx_proctoring.exceptions import (
ProctoredExamAlreadyExists, ProctoredExamAlreadyExists,
...@@ -1160,3 +1161,140 @@ class ProctoredExamApiTests(LoggedInTestCase): ...@@ -1160,3 +1161,140 @@ class ProctoredExamApiTests(LoggedInTestCase):
exam_attempt['status'], exam_attempt['status'],
ProctoredExamStudentAttemptStatus.ready_to_submit ProctoredExamStudentAttemptStatus.ready_to_submit
) )
@ddt.data(
(
ProctoredExamStudentAttemptStatus.eligible, {
'status': ProctoredExamStudentAttemptStatus.eligible,
'short_description': 'Proctored Option Available',
'suggested_icon': 'fa-lock',
'in_completed_state': False
}
),
(
ProctoredExamStudentAttemptStatus.declined, {
'status': ProctoredExamStudentAttemptStatus.declined,
'short_description': 'Taking As Open Exam',
'suggested_icon': 'fa-unlock',
'in_completed_state': False
}
),
(
ProctoredExamStudentAttemptStatus.submitted, {
'status': ProctoredExamStudentAttemptStatus.submitted,
'short_description': 'Pending Session Review',
'suggested_icon': 'fa-spinner fa-spin',
'in_completed_state': True
}
),
(
ProctoredExamStudentAttemptStatus.verified, {
'status': ProctoredExamStudentAttemptStatus.verified,
'short_description': 'Passed Proctoring',
'suggested_icon': 'fa-check',
'in_completed_state': True
}
),
(
ProctoredExamStudentAttemptStatus.rejected, {
'status': ProctoredExamStudentAttemptStatus.rejected,
'short_description': 'Failed Proctoring',
'suggested_icon': 'fa-exclamation-triangle',
'in_completed_state': True
}
),
(
ProctoredExamStudentAttemptStatus.error, {
'status': ProctoredExamStudentAttemptStatus.error,
'short_description': 'Failed Proctoring',
'suggested_icon': 'fa-exclamation-triangle',
'in_completed_state': True
}
),
(
ProctoredExamStudentAttemptStatus.created, {
'status': ProctoredExamStudentAttemptStatus.created,
'short_description': 'Taking As Proctored Exam',
'suggested_icon': 'fa-lock',
'in_completed_state': False
}
),
(
ProctoredExamStudentAttemptStatus.ready_to_start, {
'status': ProctoredExamStudentAttemptStatus.ready_to_start,
'short_description': 'Taking As Proctored Exam',
'suggested_icon': 'fa-lock',
'in_completed_state': False
}
),
(
ProctoredExamStudentAttemptStatus.started, {
'status': ProctoredExamStudentAttemptStatus.started,
'short_description': 'Taking As Proctored Exam',
'suggested_icon': 'fa-lock',
'in_completed_state': False
}
),
(
ProctoredExamStudentAttemptStatus.ready_to_submit, {
'status': ProctoredExamStudentAttemptStatus.ready_to_submit,
'short_description': 'Taking As Proctored Exam',
'suggested_icon': 'fa-lock',
'in_completed_state': False
}
)
)
@ddt.unpack
def test_attempt_status_summary(self, status, expected):
"""
Assert that we get the expected status summaries
"""
exam_attempt = self._create_started_exam_attempt()
update_attempt_status(
exam_attempt.proctored_exam_id,
self.user.id,
status
)
summary = get_attempt_status_summary(
self.user.id,
exam_attempt.proctored_exam.course_id,
exam_attempt.proctored_exam.content_id
)
self.assertIn(summary, [expected])
@ddt.data(
'honor', 'staff'
)
def test_status_summary_honor(self, enrollment_mode):
"""
Make sure status summary is None for a non-verified person
"""
set_runtime_service('credit', MockCreditService(enrollment_mode=enrollment_mode))
exam_attempt = self._create_started_exam_attempt()
summary = get_attempt_status_summary(
self.user.id,
exam_attempt.proctored_exam.course_id,
exam_attempt.proctored_exam.content_id
)
self.assertIsNone(summary)
def test_status_summary_bad(self):
"""
Make sure we get back a None when getting summary for content that does not
exist
"""
summary = get_attempt_status_summary(
self.user.id,
'foo',
'foo'
)
self.assertIsNone(summary)
...@@ -17,12 +17,12 @@ class MockCreditService(object): ...@@ -17,12 +17,12 @@ class MockCreditService(object):
Simple mock of the Credit Service Simple mock of the Credit Service
""" """
def __init__(self): def __init__(self, enrollment_mode='verified'):
""" """
Initializer Initializer
""" """
self.status = { self.status = {
'enrollment_mode': 'verified', 'enrollment_mode': enrollment_mode,
'profile_fullname': 'Wolfgang von Strucker', 'profile_fullname': 'Wolfgang von Strucker',
'credit_requirement_status': [] 'credit_requirement_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