Commit 3f2712e6 by Muhammad Shoaib

added the backbone structure and new endpoint for all the proctored exam attempts in a course.

parent 6f90af77
......@@ -66,8 +66,8 @@ class ProctoredExamStudentAttemptSerializer(serializers.ModelSerializer):
"""
Serializer for the ProctoredExamStudentAttempt Model.
"""
proctored_exam_id = serializers.IntegerField(source="proctored_exam_id")
user_id = serializers.IntegerField(required=False)
proctored_exam = ProctoredExamSerializer()
user = UserSerializer()
class Meta:
"""
......@@ -76,8 +76,8 @@ class ProctoredExamStudentAttemptSerializer(serializers.ModelSerializer):
model = ProctoredExamStudentAttempt
fields = (
"id", "created", "modified", "user_id", "started_at", "completed_at",
"external_id", "status", "proctored_exam_id", "allowed_time_limit_mins",
"id", "created", "modified", "user", "started_at", "completed_at",
"external_id", "status", "proctored_exam", "allowed_time_limit_mins",
"attempt_code", "is_sample_attempt", "taking_as_proctored"
)
......
var edx = edx || {};
(function(Backbone) {
edx.instructor_dashboard = edx.instructor_dashboard || {};
edx.instructor_dashboard.proctoring = edx.instructor_dashboard.proctoring || {};
edx.instructor_dashboard.proctoring.ProctoredExamAttemptCollection = Backbone.Collection.extend({
/* model for a collection of ProctoredExamAllowance */
model: edx.instructor_dashboard.proctoring.ProctoredExamAttemptModel,
url: '/api/edx_proctoring/v1/proctored_exam/attempt/course_id/'
});
this.edx.instructor_dashboard.proctoring.ProctoredExamAttemptCollection = edx.instructor_dashboard.proctoring.ProctoredExamAttemptCollection;
}).call(this, Backbone);
\ No newline at end of file
var edx = edx || {};
(function(Backbone) {
'use strict';
edx.instructor_dashboard = edx.instructor_dashboard || {};
edx.instructor_dashboard.proctoring = edx.instructor_dashboard.proctoring || {};
edx.instructor_dashboard.proctoring.ProctoredExamAttemptModel = Backbone.Model.extend({
url: '/api/edx_proctoring/v1/proctored_exam/allowance'
});
this.edx.instructor_dashboard.proctoring.ProctoredExamAttemptModel = edx.instructor_dashboard.proctoring.ProctoredExamAttemptModel;
}).call(this, Backbone);
......@@ -5,4 +5,9 @@ $(function() {
model: new ProctoredExamModel()
});
proctored_exam_view.render();
var proctored_exam_attempt_view = new edx.instructor_dashboard.proctoring.ProctoredExamAttemptView({
el: $('.student-proctored-exam-container'),
template_url: '/static/proctoring/templates/student-proctored-exam-attempts.underscore',
collection: new edx.instructor_dashboard.proctoring.ProctoredExamAttemptCollection()
});
});
var edx = edx || {};
(function (Backbone, $, _) {
'use strict';
edx.instructor_dashboard = edx.instructor_dashboard || {};
edx.instructor_dashboard.proctoring = edx.instructor_dashboard.proctoring || {};
edx.instructor_dashboard.proctoring.ProctoredExamAttemptView = Backbone.View.extend({
initialize: function (options) {
this.$el = options.el;
this.collection = options.collection;
this.tempate_url = options.template_url;
this.course_id = this.$el.data('course-id');
this.template = null;
this.initial_url = this.collection.url;
this.collection.url = this.initial_url + this.course_id;
/* re-render if the model changes */
this.listenTo(this.collection, 'change', this.collectionChanged);
/* Load the static template for rendering. */
this.loadTemplateData();
},
events: {
},
getCSRFToken: function () {
var cookieValue = null;
var name = 'csrftoken';
if (document.cookie && document.cookie != '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) == (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
},
loadTemplateData: function () {
var self = this;
$.ajax({url: self.tempate_url, dataType: "html"})
.error(function (jqXHR, textStatus, errorThrown) {
})
.done(function (template_data) {
self.template = _.template(template_data);
self.hydrate();
});
},
hydrate: function () {
/* This function will load the bound collection */
/* add and remove a class when we do the initial loading */
/* we might - at some point - add a visual element to the */
/* loading, like a spinner */
var self = this;
self.collection.fetch({
success: function () {
self.render();
}
});
},
collectionChanged: function () {
this.hydrate();
},
render: function () {
if (this.template !== null) {
var html = this.template(this.collection.toJSON());
this.$el.html(html);
this.$el.show();
}
}
});
this.edx.instructor_dashboard.proctoring.ProctoredExamAttemptView = edx.instructor_dashboard.proctoring.ProctoredExamAttemptView;
}).call(this, Backbone, $, _);
<div class="wrapper-content wrapper">
<section class="content">
<table class="allowance-table">
<thead>
<tr class="allowance-headings">
<th class="exam-name"><%- gettext("Exam Name") %></th>
<th class="username"><%- gettext("Username") %></th>
<th class="email"><%- gettext("Email") %></th>
<th class="allowance-name"><%- gettext("Is Exam Completed") %> </th>
<th class="allowance-value"><%- gettext("Is Exam Completed in Time") %></th>
<th class="c_action"><%- gettext("Verification Status") %> </th>
</tr>
</thead>
<tbody>
<% _.each(proctored_exam_attempts, function(proctored_exam_attempt){ %>
<tr class="allowance-items">
<td>
<%- interpolate(gettext(' %(exam_display_name)s '), { exam_display_name: proctored_exam_attempt.proctored_exam.exam_name }, true) %>
</td>
<td>
<%- interpolate(gettext(' %(username)s '), { username: proctored_exam_attempt.user.username }, true) %>
</td>
<td>
<%- interpolate(gettext(' %(email)s '), { email: proctored_exam_attempt.user.email }, true) %>
</td>
<td>
True
</td>
<td>False</td>
<td>
True
</td>
</tr>
<% }); %>
</tbody>
</table>
</section>
</div>
\ No newline at end of file
......@@ -288,8 +288,8 @@ class ProctoredExamApiTests(LoggedInTestCase):
self._create_unstarted_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)
self.assertEqual(exam_attempt['proctored_exam']['id'], self.proctored_exam_id)
self.assertEqual(exam_attempt['user']['id'], self.user_id)
def test_start_uncreated_attempt(self):
"""
......
......@@ -12,6 +12,7 @@ from django.core.urlresolvers import reverse, NoReverseMatch
from rest_framework import status
from rest_framework.response import Response
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from edx_proctoring.api import (
create_exam,
update_exam,
......@@ -26,6 +27,7 @@ from edx_proctoring.api import (
get_allowances_for_course,
get_all_exams_for_course,
get_exam_attempt_by_id,
get_all_exam_attempts
)
from edx_proctoring.exceptions import (
ProctoredBaseException,
......@@ -362,6 +364,33 @@ class StudentProctoredExamAttemptCollection(AuthenticatedAPIView):
"""
HTTP GET Handler. Returns the status of the exam attempt.
"""
if course_id is not None:
exam_attempts = get_all_exam_attempts(course_id)
paginator = Paginator(exam_attempts, 1) # Show 1 attempts per page
page = request.GET.get('page')
try:
exam_attempts_page = paginator.page(page)
except PageNotAnInteger:
# If page is not an integer, deliver first page.
exam_attempts_page = paginator.page(1)
except EmptyPage:
# If page is out of range (e.g. 9999), deliver last page of results.
exam_attempts_page = paginator.page(paginator.num_pages)
data = {
'proctored_exam_attempts': exam_attempts_page.object_list,
'pagination_info': {
'has_previous': exam_attempts_page.has_previous(),
'has_next': exam_attempts_page.has_next(),
'current_page': exam_attempts_page.number,
'total_pages': exam_attempts_page.paginator.num_pages,
}
}
return Response(
data=data,
status=status.HTTP_200_OK
)
exams = get_active_exams_for_user(request.user.id)
if exams:
......
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