Commit 617db2b2 by chrisndodge

Merge pull request #89 from edx/cdodge/detect-timer-manipulation

add computed remaining time to the API result set
parents 5ab7121e a0b4621f
...@@ -2,6 +2,9 @@ ...@@ -2,6 +2,9 @@
Helpers for the HTTP APIs Helpers for the HTTP APIs
""" """
import pytz
from datetime import datetime, timedelta
from django.utils.translation import ugettext as _ 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
...@@ -16,6 +19,23 @@ class AuthenticatedAPIView(APIView): ...@@ -16,6 +19,23 @@ class AuthenticatedAPIView(APIView):
permission_classes = (IsAuthenticated,) permission_classes = (IsAuthenticated,)
def get_time_remaining_for_attempt(attempt):
"""
Returns the remaining time (in seconds) on an attempt
"""
# need to adjust for allowances
expires_at = attempt['started_at'] + timedelta(minutes=attempt['allowed_time_limit_mins'])
now_utc = datetime.now(pytz.UTC)
if expires_at > now_utc:
time_remaining_seconds = (expires_at - now_utc).seconds
else:
time_remaining_seconds = 0
return time_remaining_seconds
def humanized_time(time_in_minutes): def humanized_time(time_in_minutes):
""" """
Converts the given value in minutes to a more human readable format Converts the given value in minutes to a more human readable format
......
...@@ -4,7 +4,7 @@ Proctored Exams HTTP-based API endpoints ...@@ -4,7 +4,7 @@ Proctored Exams HTTP-based API endpoints
import logging import logging
import pytz import pytz
from datetime import datetime, timedelta from datetime import datetime
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
from django.conf import settings from django.conf import settings
...@@ -40,7 +40,7 @@ from edx_proctoring.exceptions import ( ...@@ -40,7 +40,7 @@ from edx_proctoring.exceptions import (
from edx_proctoring.serializers import ProctoredExamSerializer, ProctoredExamStudentAttemptSerializer from edx_proctoring.serializers import ProctoredExamSerializer, ProctoredExamStudentAttemptSerializer
from edx_proctoring.models import ProctoredExamStudentAttemptStatus, ProctoredExamStudentAttempt from edx_proctoring.models import ProctoredExamStudentAttemptStatus, ProctoredExamStudentAttempt
from .utils import AuthenticatedAPIView from .utils import AuthenticatedAPIView, get_time_remaining_for_attempt
ATTEMPTS_PER_PAGE = 25 ATTEMPTS_PER_PAGE = 25
...@@ -264,7 +264,9 @@ class StudentProctoredExamAttempt(AuthenticatedAPIView): ...@@ -264,7 +264,9 @@ class StudentProctoredExamAttempt(AuthenticatedAPIView):
attempt_id=attempt_id attempt_id=attempt_id
) )
) )
raise StudentExamAttemptDoesNotExistsException(err_msg) return Response(
status=status.HTTP_400_BAD_REQUEST
)
# make sure the the attempt belongs to the calling user_id # make sure the the attempt belongs to the calling user_id
if attempt['user']['id'] != request.user.id: if attempt['user']['id'] != request.user.id:
...@@ -289,6 +291,11 @@ class StudentProctoredExamAttempt(AuthenticatedAPIView): ...@@ -289,6 +291,11 @@ class StudentProctoredExamAttempt(AuthenticatedAPIView):
ProctoredExamStudentAttemptStatus.error ProctoredExamStudentAttemptStatus.error
) )
# add in the computed time remaining as a helper to a client app
time_remaining_seconds = get_time_remaining_for_attempt(attempt)
attempt['time_remaining_seconds'] = time_remaining_seconds
return Response( return Response(
data=attempt, data=attempt,
status=status.HTTP_200_OK status=status.HTTP_200_OK
...@@ -449,14 +456,7 @@ class StudentProctoredExamAttemptCollection(AuthenticatedAPIView): ...@@ -449,14 +456,7 @@ class StudentProctoredExamAttemptCollection(AuthenticatedAPIView):
exam = exam_info['exam'] exam = exam_info['exam']
attempt = exam_info['attempt'] attempt = exam_info['attempt']
# need to adjust for allowances time_remaining_seconds = get_time_remaining_for_attempt(attempt)
expires_at = attempt['started_at'] + timedelta(minutes=attempt['allowed_time_limit_mins'])
now_utc = datetime.now(pytz.UTC)
if expires_at > now_utc:
time_remaining_seconds = (expires_at - now_utc).seconds
else:
time_remaining_seconds = 0
proctoring_settings = getattr(settings, 'PROCTORING_SETTINGS', {}) proctoring_settings = getattr(settings, 'PROCTORING_SETTINGS', {})
low_threshold_pct = proctoring_settings.get('low_threshold_pct', .2) low_threshold_pct = proctoring_settings.get('low_threshold_pct', .2)
......
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