Commit 6a1a6b7a by chrisndodge

Merge pull request #63 from edx/cdodge/add-nav-states

Cdodge/add nav states
parents 3e6edabd b1282624
......@@ -7,7 +7,11 @@ API which is in the views.py file, per edX coding standards
"""
import pytz
import uuid
import logging
from datetime import datetime, timedelta
from django.utils.translation import ugettext as _
from django.conf import settings
from django.template import Context, loader
from django.core.urlresolvers import reverse, NoReverseMatch
......@@ -36,6 +40,8 @@ from edx_proctoring.utils import humanized_time
from edx_proctoring.backends import get_backend_provider
from edx_proctoring.runtime import get_runtime_service
log = logging.getLogger(__name__)
def is_feature_enabled():
"""
......@@ -646,6 +652,89 @@ def _check_credit_eligibility(credit_state):
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):
"""
Returns a summary about the status of the attempt for the user
in the course_id and content_id
Return will be:
None: Not applicable
- or -
{
'status': ['eligible', 'declined', 'submitted', 'verified', 'rejected'],
'short_description': <short description of status>,
'suggested_icon': <recommended font-awesome icon to use>,
'in_completed_state': <if the status is considered in a 'completed' state>
}
"""
# as a quick exit, let's check credit eligibility
credit_service = get_runtime_service('credit')
if credit_service:
credit_state = credit_service.get_credit_state(user_id, unicode(course_id))
if not _check_credit_eligibility(credit_state):
return None
try:
exam = get_exam_by_content_id(course_id, content_id)
except ProctoredExamNotFoundException, ex:
# this really shouldn't happen, but log it at least
log.exception(ex)
return None
attempt = get_exam_attempt(exam['id'], user_id)
status = attempt['status'] if attempt else ProctoredExamStudentAttemptStatus.eligible
summary = None
if status in STATUS_SUMMARY_MAP:
summary = STATUS_SUMMARY_MAP[status]
else:
summary = STATUS_SUMMARY_MAP['_default']
summary.update({"status": status})
return summary
def get_student_view(user_id, course_id, content_id,
context, user_role='student'):
"""
......
......@@ -33,6 +33,7 @@ from edx_proctoring.api import (
mark_exam_attempt_timeout,
mark_exam_attempt_as_ready,
update_attempt_status,
get_attempt_status_summary,
)
from edx_proctoring.exceptions import (
ProctoredExamAlreadyExists,
......@@ -1160,3 +1161,140 @@ class ProctoredExamApiTests(LoggedInTestCase):
exam_attempt['status'],
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):
Simple mock of the Credit Service
"""
def __init__(self):
def __init__(self, enrollment_mode='verified'):
"""
Initializer
"""
self.status = {
'enrollment_mode': 'verified',
'enrollment_mode': enrollment_mode,
'profile_fullname': 'Wolfgang von Strucker',
'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