Commit 4333ba90 by chrisndodge

Merge pull request #14 from edx/muhhshoaib/PHX-47-proctoring-launch-screen

PHX-47 added the html for the proctored exam instruction template
parents 923f51b1 7e10f65d
...@@ -149,7 +149,8 @@ def get_exam_attempt(exam_id, user_id): ...@@ -149,7 +149,8 @@ def get_exam_attempt(exam_id, user_id):
Return an existing exam attempt for the given student Return an existing exam attempt for the given student
""" """
exam_attempt_obj = ProctoredExamStudentAttempt.get_exam_attempt(exam_id, user_id) exam_attempt_obj = ProctoredExamStudentAttempt.get_exam_attempt(exam_id, user_id)
return exam_attempt_obj.__dict__ if exam_attempt_obj else None serialized_attempt_obj = ProctoredExamStudentAttemptSerializer(exam_attempt_obj)
return serialized_attempt_obj.data if exam_attempt_obj else None
def create_exam_attempt(exam_id, user_id, external_id): def create_exam_attempt(exam_id, user_id, external_id):
...@@ -303,6 +304,7 @@ def get_student_view(user_id, course_id, content_id, context): ...@@ -303,6 +304,7 @@ def get_student_view(user_id, course_id, content_id, context):
student_view_template = 'proctoring/seq_proctored_exam_entrance.html' student_view_template = 'proctoring/seq_proctored_exam_entrance.html'
else: else:
student_view_template = 'proctoring/seq_proctored_exam_instructions.html' student_view_template = 'proctoring/seq_proctored_exam_instructions.html'
context.update({'exam_code': '@asDASD@E2313213SDASD213123423WEWA'})
else: else:
student_view_template = 'proctoring/seq_timed_exam_entrance.html' student_view_template = 'proctoring/seq_timed_exam_entrance.html'
elif has_finished_exam: elif has_finished_exam:
......
...@@ -46,14 +46,17 @@ class ProctoredExamStudentAttemptSerializer(serializers.ModelSerializer): ...@@ -46,14 +46,17 @@ class ProctoredExamStudentAttemptSerializer(serializers.ModelSerializer):
""" """
Serializer for the ProctoredExamStudentAttempt Model. Serializer for the ProctoredExamStudentAttempt Model.
""" """
proctored_exam_id = serializers.IntegerField(source="proctored_exam_id")
class Meta: class Meta:
""" """
Meta Class Meta Class
""" """
model = ProctoredExamStudentAttempt model = ProctoredExamStudentAttempt
fields = ( fields = (
"id", "created", "modified", "user_id", "started_at", "completed_at", "id", "created", "modified", "user_id", "started_at", "completed_at",
"external_id", "status" "external_id", "status", "proctored_exam_id"
) )
......
{% load i18n %} {% load i18n %}
<div class="sequence" data-exam-id="{{exam_id}}">
</div>
<div class="sequence proctored-exam entrance" data-exam-id="{{exam_id}}"> <div class="sequence proctored-exam entrance" data-exam-id="{{exam_id}}">
<h3> <h3>
{% blocktrans %} {% blocktrans %}
...@@ -40,6 +38,14 @@ ...@@ -40,6 +38,14 @@
<i class="fa fa-arrow-circle-right start-timed-exam" data-ajax-url="{{enter_exam_endpoint}}" data-exam-id="{{exam_id}}" data-attempt-proctored=false></i> <i class="fa fa-arrow-circle-right start-timed-exam" data-ajax-url="{{enter_exam_endpoint}}" data-exam-id="{{exam_id}}" data-attempt-proctored=false></i>
</div> </div>
</div> </div>
<div class="footer-sequence">
<h4> {% trans "Why i am seeing these options?" %} </h4>
<p>
{% blocktrans %}
Text to be added here.
{% endblocktrans %}
</p>
</div>
{% include 'proctoring/seq_proctored_exam_footer.html' %} {% include 'proctoring/seq_proctored_exam_footer.html' %}
<script type="text/javascript"> <script type="text/javascript">
......
{% load i18n %} {% load i18n %}
<div class="footer-sequence">
<h4> {% trans "Why i am seeing these options?" %} </h4>
<p>
{% blocktrans %}
Text to be added here.
{% endblocktrans %}
</p>
</div>
<div class="faq-proctoring-exam"> <div class="faq-proctoring-exam">
<h4> {% trans "See Also" %} </h4> <h4> {% trans "See Also" %} </h4>
<p> <p>
......
{% load i18n %} {% load i18n %}
<div class="sequence" data-exam-id="{{exam_id}}"> <div class="sequence proctored-exam instructions" data-exam-id="{{exam_id}}">
How to launch the proctored exam content goes here <h3>
{% blocktrans %}
Awaiting Proctoring Installation & Set Up
{% endblocktrans %}
</h3>
<p>
{% blocktrans %}
you have chosen to take {{display_name}} as a proctored exam. You should be redirected to a new window
with installation and setup instructions. You can also <a href="#">open the installation</a>
{% endblocktrans %}
</p>
<div class="proctored-exam-message">
<h3>
{% blocktrans %}
Here is your unique exam code. You'll be asked for it during the setup.
{% endblocktrans %}
</h3>
<h1> {{exam_code}}</h1>
<p>
{% blocktrans %}
Please do not share this code. It can only be used once and it tied to your edX Account.
{% endblocktrans %}
</p>
</div>
</div> </div>
<div class="footer-sequence border-b-0 padding-b-0">
<span> {% trans "Note: Once you complete your installation and set up, your timed exam will begin." %} </span>
<p>
{% blocktrans %}
Please be prepared to start the exam and follow all the guidelines of an edX proctored exam.
{% endblocktrans %}
</p>
<p class="proctored-exam-option">
{% blocktrans %}
Don't want to take this as a proctored exam? <a href="#">Take this as an open exam instead.</a>
{% endblocktrans %}
</p>
</div>
{% include 'proctoring/seq_proctored_exam_footer.html' %}
{% load i18n %} {% load i18n %}
<div class="sequence timed-exam expired"> <div class="critical-time sequence proctored-exam entrance" data-exam-id="{{exam_id}}">
<div class="gated-sequence"> <h3>
{% trans "You have run out of time!" %} {% blocktrans %}
</div> You did not complete the exam in the allotted time
{% endblocktrans %}
</h3>
<p>
{% blocktrans %}
Since you did not submit your exam before the time allotted expired, your work cannot be
considered as an exam that is eligible for credit. Any work you've completed may still be counted for
partial credit towards your grade.
{% endblocktrans %}
</p>
<div class="proctored-exam-message">
<p>
{% blocktrans %}
Please see <a href="#">your progress in this course</a> for your general course credit worthiness.
{% endblocktrans %}
</p>
</div>
</div> </div>
{% include 'proctoring/seq_timed_exam_footer.html' %} <div class="footer-sequence border-b-0 padding-b-0">
\ No newline at end of file <span> {% trans "Is there anything i can do to make up/replace this session ?" %} </span>
<p>
{% blocktrans %}
text goes here.
{% endblocktrans %}
</p>
</div>
{% include 'proctoring/seq_timed_exam_footer.html' %}
...@@ -20,7 +20,8 @@ from edx_proctoring.exceptions import ( ...@@ -20,7 +20,8 @@ from edx_proctoring.exceptions import (
ProctoredExamAlreadyExists, ProctoredExamAlreadyExists,
ProctoredExamNotFoundException, ProctoredExamNotFoundException,
StudentExamAttemptAlreadyExistsException, StudentExamAttemptAlreadyExistsException,
StudentExamAttemptDoesNotExistsException StudentExamAttemptDoesNotExistsException,
StudentExamAttemptedAlreadyStarted
) )
from edx_proctoring.models import ( from edx_proctoring.models import (
ProctoredExam, ProctoredExam,
...@@ -64,7 +65,17 @@ class ProctoredExamApiTests(LoggedInTestCase): ...@@ -64,7 +65,17 @@ class ProctoredExamApiTests(LoggedInTestCase):
time_limit_mins=self.default_time_limit time_limit_mins=self.default_time_limit
) )
def _create_student_exam_attempt(self): def _create_unstarted_exam_attempt(self):
"""
Creates the ProctoredExamStudentAttempt object.
"""
return ProctoredExamStudentAttempt.objects.create(
proctored_exam_id=self.proctored_exam_id,
user_id=self.user_id,
external_id=self.external_id
)
def _create_started_exam_attempt(self):
""" """
Creates the ProctoredExamStudentAttempt object. Creates the ProctoredExamStudentAttempt object.
""" """
...@@ -195,30 +206,54 @@ class ProctoredExamApiTests(LoggedInTestCase): ...@@ -195,30 +206,54 @@ class ProctoredExamApiTests(LoggedInTestCase):
attempt_id = create_exam_attempt(self.proctored_exam_id, self.user_id, '') attempt_id = create_exam_attempt(self.proctored_exam_id, self.user_id, '')
self.assertGreater(attempt_id, 0) self.assertGreater(attempt_id, 0)
def test_recreate_an_exam_attempt(self):
"""
Start an exam attempt that has already been created.
Raises StudentExamAttemptAlreadyExistsException
"""
proctored_exam_student_attempt = self._create_unstarted_exam_attempt()
with self.assertRaises(StudentExamAttemptAlreadyExistsException):
create_exam_attempt(proctored_exam_student_attempt.proctored_exam, self.user_id, self.external_id)
def test_get_exam_attempt(self): def test_get_exam_attempt(self):
""" """
Test to get the already made exam attempt. Test to get the already made exam attempt.
""" """
self._create_student_exam_attempt() self._create_unstarted_exam_attempt()
exam_attempt = get_exam_attempt(self.proctored_exam_id, self.user_id) exam_attempt = get_exam_attempt(self.proctored_exam_id, self.user_id)
self.assertEqual(exam_attempt['proctored_exam_id'], self.proctored_exam_id) self.assertEqual(exam_attempt['proctored_exam_id'], self.proctored_exam_id)
self.assertEqual(exam_attempt['user_id'], self.user_id) self.assertEqual(exam_attempt['user_id'], self.user_id)
def test_restart_exam_attempt(self): def test_start_uncreated_attempt(self):
""" """
Start an exam attempt that has already been started. Test to attempt starting an attempt which has not been created yet.
Raises StudentExamAttemptAlreadyExistsException should raise an exception.
""" """
proctored_exam_student_attempt = self._create_student_exam_attempt() with self.assertRaises(StudentExamAttemptDoesNotExistsException):
with self.assertRaises(StudentExamAttemptAlreadyExistsException): start_exam_attempt(self.proctored_exam_id, self.user_id)
create_exam_attempt(proctored_exam_student_attempt.proctored_exam, self.user_id, self.external_id)
def test_start_a_created_attempt(self):
"""
Test to attempt starting an attempt which has been created but not started.
"""
self._create_unstarted_exam_attempt()
start_exam_attempt(self.proctored_exam_id, self.user_id)
def test_restart_a_started_attempt(self):
"""
Test to attempt starting an attempt which has been created but not started.
"""
self._create_unstarted_exam_attempt()
start_exam_attempt(self.proctored_exam_id, self.user_id)
with self.assertRaises(StudentExamAttemptedAlreadyStarted):
start_exam_attempt(self.proctored_exam_id, self.user_id)
def test_stop_exam_attempt(self): def test_stop_exam_attempt(self):
""" """
Stop an exam attempt. Stop an exam attempt.
""" """
proctored_exam_student_attempt = self._create_student_exam_attempt() proctored_exam_student_attempt = self._create_unstarted_exam_attempt()
self.assertIsNone(proctored_exam_student_attempt.completed_at) self.assertIsNone(proctored_exam_student_attempt.completed_at)
proctored_exam_attempt_id = stop_exam_attempt( proctored_exam_attempt_id = stop_exam_attempt(
proctored_exam_student_attempt.proctored_exam, self.user_id proctored_exam_student_attempt.proctored_exam, self.user_id
...@@ -237,7 +272,7 @@ class ProctoredExamApiTests(LoggedInTestCase): ...@@ -237,7 +272,7 @@ class ProctoredExamApiTests(LoggedInTestCase):
Test to get the all the active Test to get the all the active
exams for the user. exams for the user.
""" """
active_exam_attempt = self._create_student_exam_attempt() active_exam_attempt = self._create_started_exam_attempt()
self.assertEqual(active_exam_attempt.is_active, True) self.assertEqual(active_exam_attempt.is_active, True)
exam_id = create_exam( exam_id = create_exam(
course_id=self.course_id, course_id=self.course_id,
......
...@@ -486,6 +486,14 @@ class TestStudentProctoredExamAttempt(LoggedInTestCase): ...@@ -486,6 +486,14 @@ class TestStudentProctoredExamAttempt(LoggedInTestCase):
) )
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
ProctoredExamStudentAttempt.objects.filter(
proctored_exam_id=proctored_exam.id,
user_id=self.user.id,
external_id=proctored_exam.external_id,
).update(
started_at=datetime.now(pytz.UTC)
)
response = self.client.get( response = self.client.get(
reverse('edx_proctoring.proctored_exam.attempt') reverse('edx_proctoring.proctored_exam.attempt')
) )
......
...@@ -239,7 +239,7 @@ class StudentProctoredExamAttempt(AuthenticatedAPIView): ...@@ -239,7 +239,7 @@ class StudentProctoredExamAttempt(AuthenticatedAPIView):
def post(self, request): def post(self, request):
""" """
HTTP POST handler. To create an exam. HTTP POST handler. To create an exam attempt.
""" """
start_immediately = request.DATA.get('start_clock', 'false').lower() == 'true' start_immediately = request.DATA.get('start_clock', 'false').lower() == 'true'
exam_id = request.DATA.get('exam_id', None) exam_id = request.DATA.get('exam_id', None)
......
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