Commit ac0115f7 by chrisndodge

Merge pull request #19 from edx/cdodge/add-get-exams-for-course

Add API method to get a list of all exams for a course
parents b17b841b 90853b76
...@@ -219,6 +219,17 @@ def stop_exam_attempt(exam_id, user_id): ...@@ -219,6 +219,17 @@ def stop_exam_attempt(exam_id, user_id):
return exam_attempt_obj.id return exam_attempt_obj.id
def get_all_exams_for_course(course_id):
"""
This method will return all exams for a course. This will return a list
of dictionaries, whose schema is the same as what is returned in
get_exam_by_id
"""
exams = ProctoredExam.get_all_exams_for_course(course_id)
return [ProctoredExamSerializer(proctored_exam).data for proctored_exam in exams]
def get_active_exams_for_user(user_id, course_id=None): def get_active_exams_for_user(user_id, course_id=None):
""" """
This method will return a list of active exams for the user, This method will return a list of active exams for the user,
...@@ -277,9 +288,17 @@ def get_student_view(user_id, course_id, content_id, context): ...@@ -277,9 +288,17 @@ def get_student_view(user_id, course_id, content_id, context):
exam_id = None exam_id = None
try: try:
exam = get_exam_by_content_id(course_id, content_id) exam = get_exam_by_content_id(course_id, content_id)
if not exam['is_active']:
# Exam is no longer active
# Note, we don't hard delete exams since we need to retain
# data
return None
exam_id = exam['id'] exam_id = exam['id']
is_proctored = exam['is_proctored'] is_proctored = exam['is_proctored']
except ProctoredExamNotFoundException: except ProctoredExamNotFoundException:
# This really shouldn't happen
# as Studio will be setting this up
is_proctored = context.get('is_proctored', False) is_proctored = context.get('is_proctored', False)
exam_id = create_exam( exam_id = create_exam(
course_id=course_id, course_id=course_id,
......
...@@ -65,6 +65,13 @@ class ProctoredExam(TimeStampedModel): ...@@ -65,6 +65,13 @@ class ProctoredExam(TimeStampedModel):
proctored_exam = None proctored_exam = None
return proctored_exam return proctored_exam
@classmethod
def get_all_exams_for_course(cls, course_id):
"""
Returns all exams for a give course
"""
return cls.objects.filter(course_id=course_id)
class ProctoredExamStudentAttempt(TimeStampedModel): class ProctoredExamStudentAttempt(TimeStampedModel):
""" """
......
...@@ -16,6 +16,7 @@ from edx_proctoring.api import ( ...@@ -16,6 +16,7 @@ from edx_proctoring.api import (
get_exam_attempt, get_exam_attempt,
create_exam_attempt, create_exam_attempt,
get_student_view, get_student_view,
get_all_exams_for_course,
) )
from edx_proctoring.exceptions import ( from edx_proctoring.exceptions import (
ProctoredExamAlreadyExists, ProctoredExamAlreadyExists,
...@@ -48,12 +49,14 @@ class ProctoredExamApiTests(LoggedInTestCase): ...@@ -48,12 +49,14 @@ class ProctoredExamApiTests(LoggedInTestCase):
self.default_time_limit = 21 self.default_time_limit = 21
self.course_id = 'test_course' self.course_id = 'test_course'
self.content_id = 'test_content_id' self.content_id = 'test_content_id'
self.disabled_content_id = 'test_disabled_content_id'
self.exam_name = 'Test Exam' self.exam_name = 'Test Exam'
self.user_id = 1 self.user_id = 1
self.key = 'Test Key' self.key = 'Test Key'
self.value = 'Test Value' self.value = 'Test Value'
self.external_id = 'test_external_id' self.external_id = 'test_external_id'
self.proctored_exam_id = self._create_proctored_exam() self.proctored_exam_id = self._create_proctored_exam()
self.disabled_exam_id = self._create_disabled_exam()
# Messages for get_student_view # Messages for get_student_view
self.start_an_exam_msg = 'Would you like to take %s as a Proctored Exam?' self.start_an_exam_msg = 'Would you like to take %s as a Proctored Exam?'
...@@ -72,6 +75,18 @@ class ProctoredExamApiTests(LoggedInTestCase): ...@@ -72,6 +75,18 @@ class ProctoredExamApiTests(LoggedInTestCase):
time_limit_mins=self.default_time_limit time_limit_mins=self.default_time_limit
) )
def _create_disabled_exam(self):
"""
Calls the api's create_exam to create an exam object.
"""
return create_exam(
course_id=self.course_id,
content_id=self.disabled_content_id,
exam_name=self.exam_name,
time_limit_mins=self.default_time_limit,
is_active=False
)
def _create_unstarted_exam_attempt(self): def _create_unstarted_exam_attempt(self):
""" """
Creates the ProctoredExamStudentAttempt object. Creates the ProctoredExamStudentAttempt object.
...@@ -135,7 +150,7 @@ class ProctoredExamApiTests(LoggedInTestCase): ...@@ -135,7 +150,7 @@ class ProctoredExamApiTests(LoggedInTestCase):
which will throw the exception which will throw the exception
""" """
with self.assertRaises(ProctoredExamNotFoundException): with self.assertRaises(ProctoredExamNotFoundException):
update_exam(2, exam_name='Updated Exam Name', time_limit_mins=30) update_exam(0, exam_name='Updated Exam Name', time_limit_mins=30)
def test_get_proctored_exam(self): def test_get_proctored_exam(self):
""" """
...@@ -152,6 +167,9 @@ class ProctoredExamApiTests(LoggedInTestCase): ...@@ -152,6 +167,9 @@ class ProctoredExamApiTests(LoggedInTestCase):
self.assertEqual(proctored_exam['content_id'], self.content_id) self.assertEqual(proctored_exam['content_id'], self.content_id)
self.assertEqual(proctored_exam['exam_name'], self.exam_name) self.assertEqual(proctored_exam['exam_name'], self.exam_name)
exams = get_all_exams_for_course(self.course_id)
self.assertEqual(len(exams), 2)
def test_get_invalid_proctored_exam(self): def test_get_invalid_proctored_exam(self):
""" """
test to get the exam by the invalid exam_id which will test to get the exam by the invalid exam_id which will
...@@ -159,7 +177,7 @@ class ProctoredExamApiTests(LoggedInTestCase): ...@@ -159,7 +177,7 @@ class ProctoredExamApiTests(LoggedInTestCase):
""" """
with self.assertRaises(ProctoredExamNotFoundException): with self.assertRaises(ProctoredExamNotFoundException):
get_exam_by_id(2) get_exam_by_id(0)
with self.assertRaises(ProctoredExamNotFoundException): with self.assertRaises(ProctoredExamNotFoundException):
get_exam_by_content_id('teasd', 'tewasda') get_exam_by_content_id('teasd', 'tewasda')
...@@ -321,6 +339,24 @@ class ProctoredExamApiTests(LoggedInTestCase): ...@@ -321,6 +339,24 @@ class ProctoredExamApiTests(LoggedInTestCase):
self.assertIn('data-exam-id="%d"' % self.proctored_exam_id, rendered_response) self.assertIn('data-exam-id="%d"' % self.proctored_exam_id, rendered_response)
self.assertIn(self.start_an_exam_msg % self.exam_name, rendered_response) self.assertIn(self.start_an_exam_msg % self.exam_name, rendered_response)
def test_get_disabled_student_view(self):
"""
Assert that a disabled proctored exam will not override the
student_view
"""
self.assertIsNone(
get_student_view(
user_id=self.user_id,
course_id=self.course_id,
content_id=self.disabled_content_id,
context={
'is_proctored': True,
'display_name': self.exam_name,
'default_time_limit_mins': 90
}
)
)
def test_getstudentview_timedexam(self): def test_getstudentview_timedexam(self):
""" """
Test for get_student_view Timed exam which is not proctored. Test for get_student_view Timed exam which is not proctored.
......
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