Commit bceb10b0 by Muhammad Shoaib

SOL-1085

- add new columns in the ProctoredStudentAttempt Table
- record the last polling contact info
- fixed the bug in the search bar of proctored exam attempts.
parent c1781f3b
...@@ -182,6 +182,16 @@ def get_exam_attempt_by_id(attempt_id): ...@@ -182,6 +182,16 @@ def get_exam_attempt_by_id(attempt_id):
return serialized_attempt_obj.data if exam_attempt_obj else None return serialized_attempt_obj.data if exam_attempt_obj else None
def update_exam_attempt(attempt_id, **kwargs):
"""
update exam_attempt
"""
exam_attempt_obj = ProctoredExamStudentAttempt.objects.get_exam_attempt_by_id(attempt_id)
for key, value in kwargs.items():
setattr(exam_attempt_obj, key, value)
exam_attempt_obj.save()
def get_exam_attempt_by_code(attempt_code): def get_exam_attempt_by_code(attempt_code):
""" """
Signals the beginning of an exam attempt when we only have Signals the beginning of an exam attempt when we only have
......
...@@ -5,6 +5,9 @@ Various callback paths that support callbacks from SoftwareSecure ...@@ -5,6 +5,9 @@ Various callback paths that support callbacks from SoftwareSecure
import logging import logging
from django.template import Context, loader from django.template import Context, loader
from django.http import HttpResponse from django.http import HttpResponse
import pytz
from datetime import datetime
from ipware.ip import get_ip
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from rest_framework.views import APIView from rest_framework.views import APIView
...@@ -13,7 +16,7 @@ from rest_framework.response import Response ...@@ -13,7 +16,7 @@ from rest_framework.response import Response
from edx_proctoring.api import ( from edx_proctoring.api import (
get_exam_attempt_by_code, get_exam_attempt_by_code,
mark_exam_attempt_as_ready, mark_exam_attempt_as_ready,
) update_exam_attempt)
from edx_proctoring.backends import get_backend_provider from edx_proctoring.backends import get_backend_provider
...@@ -33,7 +36,6 @@ def start_exam_callback(request, attempt_code): # pylint: disable=unused-argume ...@@ -33,7 +36,6 @@ def start_exam_callback(request, attempt_code): # pylint: disable=unused-argume
IMPORTANT: This is an unauthenticated endpoint, so be VERY CAREFUL about extending IMPORTANT: This is an unauthenticated endpoint, so be VERY CAREFUL about extending
this endpoint this endpoint
""" """
attempt = get_exam_attempt_by_code(attempt_code) attempt = get_exam_attempt_by_code(attempt_code)
if not attempt: if not attempt:
return HttpResponse( return HttpResponse(
...@@ -100,12 +102,16 @@ class AttemptStatus(APIView): ...@@ -100,12 +102,16 @@ class AttemptStatus(APIView):
""" """
attempt = get_exam_attempt_by_code(attempt_code) attempt = get_exam_attempt_by_code(attempt_code)
ip_address = get_ip(request)
timestamp = datetime.now(pytz.UTC)
if not attempt: if not attempt:
return HttpResponse( return HttpResponse(
content='You have entered an exam code that is not valid.', content='You have entered an exam code that is not valid.',
status=404 status=404
) )
update_exam_attempt(attempt['id'], last_poll_timestamp=timestamp, last_poll_ipaddr=ip_address)
return Response( return Response(
data={ data={
# IMPORTANT: Don't add more information to this as it is an # IMPORTANT: Don't add more information to this as it is an
......
...@@ -199,6 +199,9 @@ class ProctoredExamStudentAttempt(TimeStampedModel): ...@@ -199,6 +199,9 @@ class ProctoredExamStudentAttempt(TimeStampedModel):
started_at = models.DateTimeField(null=True) started_at = models.DateTimeField(null=True)
completed_at = models.DateTimeField(null=True) completed_at = models.DateTimeField(null=True)
last_poll_timestamp = models.DateTimeField(null=True)
last_poll_ipaddr = models.CharField(max_length=32, null=True)
# this will be a unique string ID that the user # this will be a unique string ID that the user
# will have to use when starting the proctored exam # will have to use when starting the proctored exam
attempt_code = models.CharField(max_length=255, null=True, db_index=True) attempt_code = models.CharField(max_length=255, null=True, db_index=True)
......
<div class="wrapper-content wrapper"> <div class="wrapper-content wrapper">
<% var is_proctored_attempts = proctored_exam_attempts.length !== 0 %> <% var is_proctored_attempts = proctored_exam_attempts.length !== 0 %>
<% if (is_proctored_attempts) { %>
<section class="content"> <section class="content">
<div class="top-header"> <div class="top-header">
<div class='search-attempts'> <div class='search-attempts'>
...@@ -89,6 +88,7 @@ ...@@ -89,6 +88,7 @@
<th class="c_action"><%- gettext("Actions") %> </th> <th class="c_action"><%- gettext("Actions") %> </th>
</tr> </tr>
</thead> </thead>
<% if (is_proctored_attempts) { %>
<tbody> <tbody>
<% _.each(proctored_exam_attempts, function(proctored_exam_attempt){ %> <% _.each(proctored_exam_attempts, function(proctored_exam_attempt){ %>
<tr class="allowance-items"> <tr class="allowance-items">
...@@ -118,9 +118,11 @@ ...@@ -118,9 +118,11 @@
</tr> </tr>
<% }); %> <% }); %>
</tbody> </tbody>
<% } %>
</table> </table>
<% if (!is_proctored_attempts) { %>
<p> No exam results found.
<% } %>
</section> </section>
<% } else { %>
<p> No exam results found.
<% } %>
</div> </div>
...@@ -943,7 +943,8 @@ class TestStudentProctoredExamAttempt(LoggedInTestCase): ...@@ -943,7 +943,8 @@ class TestStudentProctoredExamAttempt(LoggedInTestCase):
) )
) )
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(response.data['status'], 'ready_to_start') response_data = json.loads(response.content)
self.assertEqual(response_data['status'], 'ready_to_start')
def test_bad_exam_code_callback(self): def test_bad_exam_code_callback(self):
""" """
......
...@@ -5,3 +5,6 @@ South>=0.7.6 ...@@ -5,3 +5,6 @@ South>=0.7.6
djangorestframework>=2.3.5,<=2.3.14 djangorestframework>=2.3.5,<=2.3.14
pytz>=2012h pytz>=2012h
pycrypto>=2.6 pycrypto>=2.6
# Third Party
-e git+https://github.com/un33k/django-ipware.git@42cb1bb1dc680a60c6452e8bb2b843c2a0382c90#egg=django-ipware
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