Commit cfe4923d by chrisndodge

Merge pull request #9 from edx/muhhshoaib/PHX-42-change-the-timed-exam-entrance-html-file

PHX-42 added the html for the entrance timed exam.
parents 120755b9 8a40531b
...@@ -19,6 +19,7 @@ from edx_proctoring.models import ( ...@@ -19,6 +19,7 @@ from edx_proctoring.models import (
) )
from edx_proctoring.serializers import ProctoredExamSerializer, ProctoredExamStudentAttemptSerializer, \ from edx_proctoring.serializers import ProctoredExamSerializer, ProctoredExamStudentAttemptSerializer, \
ProctoredExamStudentAllowanceSerializer ProctoredExamStudentAllowanceSerializer
from edx_proctoring.utils import humanized_time
def create_exam(course_id, content_id, exam_name, time_limit_mins, def create_exam(course_id, content_id, exam_name, time_limit_mins,
...@@ -252,7 +253,9 @@ def get_student_view(user_id, course_id, content_id, context): ...@@ -252,7 +253,9 @@ def get_student_view(user_id, course_id, content_id, context):
if student_view_template: if student_view_template:
template = loader.get_template(student_view_template) template = loader.get_template(student_view_template)
django_context = Context(context) django_context = Context(context)
total_time = humanized_time(context['default_time_limit_mins'])
django_context.update({ django_context.update({
'total_time': total_time,
'exam_id': exam_id, 'exam_id': exam_id,
'enter_exam_endpoint': reverse('edx_proctoring.proctored_exam.attempt'), 'enter_exam_endpoint': reverse('edx_proctoring.proctored_exam.attempt'),
}) })
......
{% load i18n %}
<div class="sequence" > <div class="sequence" >
<div class="gated-sequence"> <div class="gated-sequence">
All done! {% trans "All done!" %}
</div> </div>
</div> </div>
{% include 'proctoring/seq_timed_exam_footer.html' %}
{% load i18n %}
<div class="sequence" data-exam-id="{{exam_id}}"> <div class="sequence" data-exam-id="{{exam_id}}">
<div class="gated-sequence"> <h3>
This is a timed exam. Would you like to <a class='start-timed-exam' data-ajax-url="{{enter_exam_endpoint}}">enter</a> it? {% blocktrans %}
</div> {{ display_name }} is a Timed Exam ({{total_time}})
{% endblocktrans %}
</h3>
<p>
{% trans "This exam has a time limit associated with it." %}
<strong>
{% trans "In order to successfully pass this exam you will have to answer the following questions and problems in the time allotted." %}
</strong>
{% blocktrans %}
Once you proceed, you'll start both the exam and the {{total_time|lower}} given to you.
{% endblocktrans %}
</p>
<div class="gated-sequence">
<a class='start-timed-exam' data-ajax-url="{{enter_exam_endpoint}}" data-exam-id="{{exam_id}}">
{% blocktrans %}
I'm ready! Start this timed exam.
{% endblocktrans %}
<i class="fa fa-arrow-circle-right"></i>
</a>
</div>
</div> </div>
{% include 'proctoring/seq_timed_exam_footer.html' %}
<script type="text/javascript"> <script type="text/javascript">
$('.start-timed-exam').click( $('.start-timed-exam').click(
function(event) { function(event) {
var target = $(event.target); var action_url = $(this).data('ajax-url');
var action_url = target.data('ajax-url'); var exam_id = $(this).data('exam-id');
var exam_id = target.parent().parent().data('exam-id');
$.post( $.post(
action_url, action_url,
{ {
"exam_id": exam_id, "exam_id": exam_id
}, },
function(data) { function(data) {
// reload the page, because we've unlocked it // reload the page, because we've unlocked it
......
{% load i18n %}
<div class="sequence"> <div class="sequence">
<div class="gated-sequence"> <div class="gated-sequence">
You have run out of time! {% trans "You have run out of time!" %}
</div> </div>
</div> </div>
{% include 'proctoring/seq_timed_exam_footer.html' %}
\ No newline at end of file
{% load i18n %}
<div class="footer-sequence">
<h4> {% trans "What if i need more time " %}? </h4>
<p>{% trans "edX ocassionally grants more time for students with disabilities and difficult conditions. " %}
{% blocktrans %}
Please see <a href="#">
our frequently asked questions about timed exams and more policies.
</a>
{% endblocktrans %}
</p>
</div>
\ No newline at end of file
...@@ -3,11 +3,29 @@ All tests for the models.py ...@@ -3,11 +3,29 @@ All tests for the models.py
""" """
from datetime import datetime from datetime import datetime
import pytz import pytz
from edx_proctoring.api import create_exam, update_exam, get_exam_by_id, get_exam_by_content_id, \ from edx_proctoring.api import (
add_allowance_for_user, remove_allowance_for_user, start_exam_attempt, stop_exam_attempt, get_active_exams_for_user create_exam,
from edx_proctoring.exceptions import ProctoredExamAlreadyExists, ProctoredExamNotFoundException, \ update_exam,
StudentExamAttemptAlreadyExistsException, StudentExamAttemptDoesNotExistsException get_exam_by_id,
from edx_proctoring.models import ProctoredExam, ProctoredExamStudentAllowance, ProctoredExamStudentAttempt get_exam_by_content_id,
add_allowance_for_user,
remove_allowance_for_user,
start_exam_attempt,
stop_exam_attempt,
get_active_exams_for_user,
get_exam_attempt
)
from edx_proctoring.exceptions import (
ProctoredExamAlreadyExists,
ProctoredExamNotFoundException,
StudentExamAttemptAlreadyExistsException,
StudentExamAttemptDoesNotExistsException
)
from edx_proctoring.models import (
ProctoredExam,
ProctoredExamStudentAllowance,
ProctoredExamStudentAttempt
)
from .utils import ( from .utils import (
LoggedInTestCase LoggedInTestCase
...@@ -176,6 +194,16 @@ class ProctoredExamApiTests(LoggedInTestCase): ...@@ -176,6 +194,16 @@ class ProctoredExamApiTests(LoggedInTestCase):
attempt_id = start_exam_attempt(self.proctored_exam_id, self.user_id, self.external_id) attempt_id = start_exam_attempt(self.proctored_exam_id, self.user_id, self.external_id)
self.assertGreater(attempt_id, 0) self.assertGreater(attempt_id, 0)
def test_get_exam_attempt(self):
"""
Test to get the already made exam attempt.
"""
self._create_student_exam_attempt()
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['user_id'], self.user_id)
def test_restart_exam_attempt(self): def test_restart_exam_attempt(self):
""" """
Start an exam attempt that has already been started. Start an exam attempt that has already been started.
......
"""
File that contains tests for the util methods.
"""
import unittest
from edx_proctoring.utils import humanized_time
class TestHumanizedTime(unittest.TestCase):
"""
Class to test the humanized_time utility function
"""
def test_humanized_time(self):
"""
tests the humanized_time utility function against different values.
"""
human_time = humanized_time(0)
self.assertEqual(human_time, "0 Minutes")
human_time = humanized_time(1)
self.assertEqual(human_time, "1 Minute")
human_time = humanized_time(10)
self.assertEqual(human_time, "10 Minutes")
human_time = humanized_time(60)
self.assertEqual(human_time, "1 Hour")
human_time = humanized_time(61)
self.assertEqual(human_time, "1 Hour and 1 Minute")
human_time = humanized_time(62)
self.assertEqual(human_time, "1 Hour and 2 Minutes")
human_time = humanized_time(120)
self.assertEqual(human_time, "2 Hours")
human_time = humanized_time(121)
self.assertEqual(human_time, "2 Hours and 1 Minute")
human_time = humanized_time(150)
self.assertEqual(human_time, "2 Hours and 30 Minutes")
human_time = humanized_time(180)
self.assertEqual(human_time, "3 Hours")
...@@ -49,26 +49,6 @@ class ProctoredExamsApiTests(LoggedInTestCase): ...@@ -49,26 +49,6 @@ class ProctoredExamsApiTests(LoggedInTestCase):
self.assertEqual(response.status_code, 403) self.assertEqual(response.status_code, 403)
class StudentProctoredExamAttempt(LoggedInTestCase):
"""
Tests for StudentProctoredExamAttempt
"""
def setUp(self):
super(StudentProctoredExamAttempt, self).setUp()
self.user.is_staff = True
self.user.save()
self.client.login_user(self.user)
def test_get_exam_attempt(self):
"""
Test Case for retrieving student proctored exam attempt status.
"""
response = self.client.get(
reverse('edx_proctoring.proctored_exam.attempt')
)
self.assertEqual(response.status_code, 200)
class ProctoredExamViewTests(LoggedInTestCase): class ProctoredExamViewTests(LoggedInTestCase):
""" """
Tests for the ProctoredExamView Tests for the ProctoredExamView
...@@ -476,6 +456,81 @@ class TestStudentProctoredExamAttempt(LoggedInTestCase): ...@@ -476,6 +456,81 @@ class TestStudentProctoredExamAttempt(LoggedInTestCase):
response_data = json.loads(response.content) response_data = json.loads(response.content)
self.assertEqual(response_data['detail'], 'Error. Trying to stop an exam that is not in progress.') self.assertEqual(response_data['detail'], 'Error. Trying to stop an exam that is not in progress.')
def test_get_exam_attempt(self):
"""
Test Case for retrieving student proctored exam attempt status.
"""
# Create an exam.
proctored_exam = ProctoredExam.objects.create(
course_id='a/b/c',
content_id='test_content',
exam_name='Test Exam',
external_id='123aXqe3',
time_limit_mins=90
)
response = self.client.get(
reverse('edx_proctoring.proctored_exam.attempt')
)
self.assertEqual(response.status_code, 200)
attempt_data = {
'exam_id': proctored_exam.id,
'user_id': self.student_taking_exam.id,
'external_id': proctored_exam.external_id
}
response = self.client.post(
reverse('edx_proctoring.proctored_exam.attempt'),
attempt_data
)
self.assertEqual(response.status_code, 200)
response = self.client.get(
reverse('edx_proctoring.proctored_exam.attempt')
)
self.assertEqual(response.status_code, 200)
def test_get_expired_exam_attempt(self):
"""
Test to get the exam the time for which has finished.
"""
# Create an exam.
proctored_exam = ProctoredExam.objects.create(
course_id='a/b/c',
content_id='test_content',
exam_name='Test Exam',
external_id='123aXqe3',
time_limit_mins=90
)
response = self.client.get(
reverse('edx_proctoring.proctored_exam.attempt')
)
self.assertEqual(response.status_code, 200)
attempt_data = {
'exam_id': proctored_exam.id,
'user_id': self.student_taking_exam.id,
'external_id': proctored_exam.external_id
}
response = self.client.post(
reverse('edx_proctoring.proctored_exam.attempt'),
attempt_data
)
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).replace(year=2013)
)
response = self.client.get(
reverse('edx_proctoring.proctored_exam.attempt')
)
self.assertEqual(response.status_code, 200)
class TestExamAllowanceView(LoggedInTestCase): class TestExamAllowanceView(LoggedInTestCase):
""" """
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
Helpers for the HTTP APIs Helpers for the HTTP APIs
""" """
from django.utils.translation import ugettext as _
from rest_framework.views import APIView from rest_framework.views import APIView
from rest_framework.authentication import SessionAuthentication from rest_framework.authentication import SessionAuthentication
from rest_framework.permissions import IsAuthenticated from rest_framework.permissions import IsAuthenticated
...@@ -13,3 +14,45 @@ class AuthenticatedAPIView(APIView): ...@@ -13,3 +14,45 @@ class AuthenticatedAPIView(APIView):
""" """
authentication_classes = (SessionAuthentication,) authentication_classes = (SessionAuthentication,)
permission_classes = (IsAuthenticated,) permission_classes = (IsAuthenticated,)
def humanized_time(time_in_minutes):
"""
Converts the given value in minutes to a more human readable format
1 -> 1 Minute
2 -> 2 Minutes
60 -> 1 hour
90 -> 1 hour and 30 Minutes
120 -> 2 hours
"""
hours = int(time_in_minutes / 60)
minutes = time_in_minutes % 60
template = ""
hours_present = False
if hours == 0:
hours_present = False
elif hours == 1:
template = _("{num_of_hours} Hour")
hours_present = True
elif hours >= 2:
template = _("{num_of_hours} Hours")
hours_present = True
if minutes == 0:
if not hours_present:
template = _("{num_of_minutes} Minutes")
elif minutes == 1:
if hours_present:
template += _(" and {num_of_minutes} Minute")
else:
template += _("{num_of_minutes} Minute")
elif minutes >= 2:
if hours_present:
template += _(" and {num_of_minutes} Minutes")
else:
template += _("{num_of_minutes} Minutes")
human_time = template.format(num_of_hours=hours, num_of_minutes=minutes)
return human_time
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