Commit 940d24bc by Muhammad Shoaib

added the pagination for the student attempts

parent a7f4c56a
...@@ -26,7 +26,17 @@ var edx = edx || {}; ...@@ -26,7 +26,17 @@ var edx = edx || {};
this.loadTemplateData(); this.loadTemplateData();
}, },
events: { events: {
"click .remove-attempt": "onRemoveAttempt" "click .remove-attempt": "onRemoveAttempt",
'click li > a.target-link': 'getPaginatedAttempts'
},
getPaginatedAttempts: function(event) {
var target = $(event.currentTarget);
var url = target.data('target-url');
this.collection.url = url;
this.hydrate();
event.stopPropagation();
event.preventDefault();
}, },
getCSRFToken: function () { getCSRFToken: function () {
var cookieValue = null; var cookieValue = null;
...@@ -73,7 +83,11 @@ var edx = edx || {}; ...@@ -73,7 +83,11 @@ var edx = edx || {};
}, },
render: function () { render: function () {
if (this.template !== null) { if (this.template !== null) {
var html = this.template({proctored_exam_attempts: this.collection.toJSON()[0].proctored_exam_attempts}); var html = this.template({
proctored_exam_attempts: this.collection.toJSON()[0].proctored_exam_attempts,
pagination_info: this.collection.toJSON()[0].pagination_info,
attempt_url: this.collection.toJSON()[0].attempt_url
});
this.$el.html(html); this.$el.html(html);
this.$el.show(); this.$el.show();
} }
......
<div class="wrapper-content wrapper"> <div class="wrapper-content wrapper">
<section class="content"> <section class="content">
<%
var humanized_time = function(time_in_minutes) {
var hours = parseInt(time_in_minutes / 60);
var minutes = time_in_minutes % 60;
<table class="allowance-table"> var hours_present = false;
<thead> if (hours == 0) {
<tr class="exam-attempt-headings"> hours_present = false;
<th class="username"><%- gettext("Username") %></th> var template = ""
<th class="attempt-exam-name"><%- gettext("Exam Name") %></th> }
<th class="attempt-allowed-time"><%- gettext("Allowed Time for Student") %> </th>
<th class="attempt-started-at"><%- gettext("started At") %></th> else if (hours == 1) {
<th class="attempt-completed-at"><%- gettext("Completed At") %> </th> template = hours + " Hour ";
<th class="attempt-status"><%- gettext("Status") %> </th> hours_present = true;
<th class="attempt-action"><%- gettext("Action") %> </th> }
</tr>
</thead> else if (hours >= 2) {
<tbody> console.log(hours);
<% _.each(proctored_exam_attempts, function(proctored_exam_attempt){ %> template = hours + " Hours ";
<tr class="allowance-items"> hours_present = true
<td> }
<%- interpolate(gettext(' %(username)s '), { username: proctored_exam_attempt.user.username }, true) %> else {
</td> template = "error";
<td> }
<%- interpolate(gettext(' %(exam_display_name)s '), { exam_display_name: proctored_exam_attempt.proctored_exam.exam_name }, true) %>
</td> if (template !== "error") {
<td> <%= proctored_exam_attempt.allowed_time_limit_mins %></td> if (minutes == 0) {
<td> if (!hours_present) {
<% if (proctored_exam_attempt.started_at){ %> template = minutes + " Minutes";
<%= proctored_exam_attempt.started_at %> }
<% } else { %> }
N/A else if ( minutes == 1 ) {
<% } %> if (hours_present) {
</td> template = template + " and " +minutes + " Minute";
<td> }
<% if (proctored_exam_attempt.completed_at){ %> else {
<%= proctored_exam_attempt.completed_at %> </td> template = template + minutes + " Minute";
<% } else { %> }
N/A }
<% } %> else {
<td> if (hours_present) {
<% if (proctored_exam_attempt.status){ %> template = template + " and " + minutes + " Minutes";
<%= proctored_exam_attempt.status %> </td> }
<% } else { %> else {
N/A template = template + minutes + " Minutes";
<% } %> }
<td> }
<% if (proctored_exam_attempt.status){ %> }
<button type="button" class="remove-attempt blue-button" data-attempt-id="<%= proctored_exam_attempt.id %>" ><%- gettext("Remove") %></button> return template;
<% } else { %> }
N/A %>
<% } %> <ul class="pagination">
</td> <% if (!pagination_info.has_previous){ %>
<li class="disabled">
<a aria-label="Previous">
<span aria-hidden="true">&laquo;</span>
</a>
</li>
<% } else { %>
<li>
<a class="target-link " data-target-url="
<%- interpolate(
'%(attempt_url)s?page=%(count)s ',
{
attempt_url: attempt_url,
count: pagination_info.current_page - 1
},
true
) %> "
href="#" aria-label="Previous">
<span aria-hidden="true">&laquo;</span>
</a>
</li>
<% }%>
<% for(var n = 1; n <= pagination_info.total_pages; n++) { %>
<li>
<a class="target-link <% if (pagination_info.current_page == n){ %> active <% } %>"
data-target-url="
<%- interpolate(
'%(attempt_url)s?page=%(count)s ',
{
attempt_url: attempt_url,
count: n
},
true
) %>
"
href="#"><%= n %>
</a>
</li>
<% } %>
<% if (!pagination_info.has_next){ %>
<li class="disabled">
<a aria-label="Next">
<span aria-hidden="true">&raquo;</span>
</a>
</li>
<% } else { %>
<li>
<a class="target-link" href="#" aria-label="Next" data-target-url="
<%- interpolate(
'%(attempt_url)s?page=%(count)s ',
{
attempt_url: attempt_url,
count: pagination_info.current_page + 1
},
true
) %> "
>
<span aria-hidden="true">&raquo;</span>
</a>
</li>
<% }%>
</ul>
<table class="exam-attempts-table">
<thead>
<tr class="exam-attempt-headings">
<th class="username"><%- gettext("Username") %></th>
<th class="exam-name"><%- gettext("Exam Name") %></th>
<th class="attempt-allowed-time"><%- gettext("Allowed Time for Student") %> </th>
<th class="attempt-started-at"><%- gettext("started At") %></th>
<th class="attempt-completed-at"><%- gettext("Completed At") %> </th>
<th class="attempt-status"><%- gettext("Status") %> </th>
<th class="c_action"><%- gettext("Action") %> </th>
</tr> </tr>
<% }); %> </thead>
</tbody> <tbody>
</table> <% _.each(proctored_exam_attempts, function(proctored_exam_attempt){ %>
</section> <tr class="allowance-items">
<td>
<%- interpolate(gettext(' %(username)s '), { username: proctored_exam_attempt.user.username }, true) %>
</td>
<td>
<%- interpolate(gettext(' %(exam_display_name)s '), { exam_display_name: proctored_exam_attempt.proctored_exam.exam_name }, true) %>
</td>
<td> <%= humanized_time(proctored_exam_attempt.allowed_time_limit_mins) %></td>
<td>
<% if (proctored_exam_attempt.started_at){ %>
<%= new Date(proctored_exam_attempt.started_at).toString("MMMM dd, yyyy HH:MM") %>
<% } else { %>
N/A
<% } %>
</td>
<td>
<% if (proctored_exam_attempt.completed_at){ %>
<%= new Date(proctored_exam_attempt.completed_at).toString("MMMM dd, yyyy HH:MM") %>
<% } else { %>
N/A
<% } %>
<td>
<% if (proctored_exam_attempt.status){ %>
<%= proctored_exam_attempt.status %> </td>
<% } else { %>
N/A
<% } %>
<td>
<% if (proctored_exam_attempt.status){ %>
<td><button type="button" class="remove-attempt blue-button" data-attempt-id="<%= proctored_exam_attempt.id %>" ><%- gettext("Remove") %></button></td>
<% } else { %>
N/A
<% } %>
</td>
</tr>
<% }); %>
</tbody>
</table>
</section>
</div> </div>
\ No newline at end of file
...@@ -22,7 +22,9 @@ from edx_proctoring.api import ( ...@@ -22,7 +22,9 @@ from edx_proctoring.api import (
get_allowances_for_course, get_allowances_for_course,
get_all_exams_for_course, get_all_exams_for_course,
get_exam_attempt_by_id, get_exam_attempt_by_id,
remove_exam_attempt_by_id) remove_exam_attempt_by_id,
get_all_exam_attempts
)
from edx_proctoring.exceptions import ( from edx_proctoring.exceptions import (
ProctoredExamAlreadyExists, ProctoredExamAlreadyExists,
ProctoredExamNotFoundException, ProctoredExamNotFoundException,
...@@ -384,6 +386,26 @@ class ProctoredExamApiTests(LoggedInTestCase): ...@@ -384,6 +386,26 @@ class ProctoredExamApiTests(LoggedInTestCase):
self.assertEqual(len(student_active_exams[0]['allowances']), 2) self.assertEqual(len(student_active_exams[0]['allowances']), 2)
self.assertEqual(len(student_active_exams[1]['allowances']), 0) self.assertEqual(len(student_active_exams[1]['allowances']), 0)
def test_get_all_exam_attempts(self):
"""
Test to get all the exam attempts.
"""
exam_attempt = self._create_started_exam_attempt()
exam_id = create_exam(
course_id=self.course_id,
content_id='test_content_2',
exam_name='Final Test Exam',
time_limit_mins=self.default_time_limit
)
updated_exam_attempt_id = create_exam_attempt(
exam_id=exam_id,
user_id=self.user_id
)
all_exams = get_all_exam_attempts(self.course_id)
self.assertEqual(len(all_exams), 2)
self.assertEqual(all_exams[0]['id'], exam_attempt.id)
self.assertEqual(all_exams[1]['id'], updated_exam_attempt_id)
def test_get_student_view(self): def test_get_student_view(self):
""" """
Test for get_student_view promting the user to take the exam Test for get_student_view promting the user to take the exam
......
...@@ -587,6 +587,36 @@ class TestStudentProctoredExamAttempt(LoggedInTestCase): ...@@ -587,6 +587,36 @@ class TestStudentProctoredExamAttempt(LoggedInTestCase):
response_data = json.loads(response.content) response_data = json.loads(response.content)
self.assertEqual(response_data['exam_attempt_id'], old_attempt_id) self.assertEqual(response_data['exam_attempt_id'], old_attempt_id)
def test_get_exam_attempts_in_a_course(self):
"""
Test to get the exam attempts in a course.
"""
# 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
)
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.collection'),
attempt_data
)
self.assertEqual(response.status_code, 200)
response = self.client.get(
reverse('edx_proctoring.proctored_exam.attempt', kwargs={'course_id': proctored_exam.course_id})
)
self.assertEqual(response.status_code, 200)
response_data = json.loads(response.content)
self.assertEqual(len(response_data['proctored_exam_attempts']), 1)
def test_stop_others_attempt(self): def test_stop_others_attempt(self):
""" """
Start an exam (create an exam attempt) Start an exam (create an exam attempt)
......
...@@ -5,6 +5,7 @@ Proctored Exams HTTP-based API endpoints ...@@ -5,6 +5,7 @@ Proctored Exams HTTP-based API endpoints
import logging import logging
import pytz import pytz
from datetime import datetime, timedelta from datetime import datetime, timedelta
from django.core.urlresolvers import reverse
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
from django.conf import settings from django.conf import settings
...@@ -393,6 +394,8 @@ class StudentProctoredExamAttemptCollection(AuthenticatedAPIView): ...@@ -393,6 +394,8 @@ class StudentProctoredExamAttemptCollection(AuthenticatedAPIView):
""" """
if course_id is not None: if course_id is not None:
exam_attempts = get_all_exam_attempts(course_id) exam_attempts = get_all_exam_attempts(course_id)
# TODO have to change the default attempts per page
paginator = Paginator(exam_attempts, 1) # Show 1 attempts per page paginator = Paginator(exam_attempts, 1) # Show 1 attempts per page
page = request.GET.get('page') page = request.GET.get('page')
try: try:
...@@ -411,7 +414,9 @@ class StudentProctoredExamAttemptCollection(AuthenticatedAPIView): ...@@ -411,7 +414,9 @@ class StudentProctoredExamAttemptCollection(AuthenticatedAPIView):
'has_next': exam_attempts_page.has_next(), 'has_next': exam_attempts_page.has_next(),
'current_page': exam_attempts_page.number, 'current_page': exam_attempts_page.number,
'total_pages': exam_attempts_page.paginator.num_pages, 'total_pages': exam_attempts_page.paginator.num_pages,
} },
'attempt_url': reverse('edx_proctoring.proctored_exam.attempt', args=[course_id])
} }
return Response( return Response(
data=data, data=data,
......
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