Commit 1b465d1b by Brian Wilson

implement testcenter_login

parent b6ae62e3
import datetime
import feedparser
#import itertools
import json
import logging
import random
import string
import sys
#import time
import urllib
import uuid
......@@ -18,10 +16,13 @@ from django.contrib.auth.models import User
from django.contrib.auth.decorators import login_required
from django.core.context_processors import csrf
from django.core.mail import send_mail
from django.core.urlresolvers import reverse
from django.core.validators import validate_email, validate_slug, ValidationError
from django.db import IntegrityError
from django.http import HttpResponse, HttpResponseForbidden, Http404
from django.http import HttpResponse, HttpResponseForbidden, Http404,\
HttpResponseRedirect
from django.shortcuts import redirect
from mitxmako.shortcuts import render_to_response, render_to_string
from bs4 import BeautifulSoup
from django.core.cache import cache
......@@ -39,11 +40,11 @@ from xmodule.course_module import CourseDescriptor
from xmodule.modulestore.exceptions import ItemNotFoundError
from xmodule.modulestore.django import modulestore
#from datetime import date
from collections import namedtuple
from courseware.courses import get_courses, sort_by_announcement
from courseware.access import has_access
from courseware.models import TimedModule
from statsd import statsd
......@@ -1058,7 +1059,7 @@ def accept_name_change(request):
# TODO: This is a giant kludge to give Pearson something to test against ASAP.
# Will need to get replaced by something that actually ties into TestCenterUser
@csrf_exempt
def test_center_login(request):
def atest_center_login(request):
if not settings.MITX_FEATURES.get('ENABLE_PEARSON_HACK_TEST'):
raise Http404
......@@ -1076,6 +1077,125 @@ def test_center_login(request):
return HttpResponseForbidden()
@csrf_exempt
def test_center_login(request):
# errors are returned by navigating to the error_url, adding a query parameter named "code"
# which contains the error code describing the exceptional condition.
def makeErrorURL(error_url, error_code):
return "{}&code={}".format(error_url, error_code);
# get provided error URL, which will be used as a known prefix for returning error messages to the
# Pearson shell. It does not have a trailing slash, so we need to add one when creating output URLs.
error_url = request.POST.get("errorURL")
# check that the parameters have not been tampered with, by comparing the code provided by Pearson
# with the code we calculate for the same parameters.
if 'code' not in request.POST:
return HttpResponseRedirect(makeErrorURL(error_url, "missingSecurityCode"));
code = request.POST.get("code")
# calculate SHA for query string
# TODO: figure out how to get the original query string, so we can hash it and compare.
if 'clientCandidateID' not in request.POST:
return HttpResponseRedirect(makeErrorURL(error_url, "missingClientCandidateID"));
client_candidate_id = request.POST.get("clientCandidateID")
# TODO: check remaining parameters, and maybe at least log if they're not matching
# expected values....
# registration_id = request.POST.get("registrationID")
# exit_url = request.POST.get("exitURL")
# find testcenter_user that matches the provided ID:
try:
testcenteruser = TestCenterUser.objects.get(client_candidate_id=client_candidate_id)
except TestCenterUser.DoesNotExist:
return HttpResponseRedirect(makeErrorURL(error_url, "invalidClientCandidateID"));
# find testcenter_registration that matches the provided exam code:
# Note that we could rely on either the registrationId or the exam code,
# or possibly both.
if 'vueExamSeriesCode' not in request.POST:
# TODO: confirm this error code (made up, not in documentation)
return HttpResponseRedirect(makeErrorURL(error_url, "missingExamSeriesCode"));
exam_series_code = request.POST.get('vueExamSeriesCode')
registrations = TestCenterRegistration.objects.filter(testcenter_user=testcenteruser, exam_series_code=exam_series_code)
if not registrations:
return HttpResponseRedirect(makeErrorURL(error_url, "noTestsAssigned"));
# TODO: figure out what to do if there are more than one registrations....
# for now, just take the first...
registration = registrations[0]
course_id = registration.course_id
# if we want to look up whether the test has already been taken, or to
# communicate that a time accommodation needs to be applied, we need to
# know the module_id to use that corresponds to the particular exam_series_code.
# For now, we can hardcode that...
if exam_series_code == '6002x001':
chapter_url_name = 'Final_Exam'
section_url_name = 'Final_Exam_Fall_2012'
redirect_url = reverse('courseware_section', args=[course_id, chapter_url_name, section_url_name])
location = 'i4x://MITx/6.002x/2012_Fall/sequence/Final_Exam_Fall_2012'
else:
# TODO: clarify if this is the right error code for this condition.
return HttpResponseRedirect(makeErrorURL(error_url, "incorrectCandidateTests"));
time_accommodation_mapping = {'ET12ET' : 'ADDHALFTIME',
'ET30MN' : 'ADD30MIN',
'ETDBTM' : 'ADDDOUBLE', }
# check if the test has already been taken
timed_modules = TimedModule.objects.filter(student=testcenteruser.user, course_id=course_id, module_state_key=location)
if timed_modules:
timed_module = timed_modules[0]
if timed_module.has_ended:
return HttpResponseRedirect(makeErrorURL(error_url, "allTestsTaken"));
elif registration.get_accommodation_codes():
# we don't have a timed module created yet, so if we have time accommodations
# to implement, create an entry now:
time_accommodation_code = None
for code in registration.get_accommodation_codes():
if code in time_accommodation_mapping:
time_accommodation_code = time_accommodation_mapping[code]
if client_candidate_id == "edX003671291147":
time_accommodation_code = 'TESTING'
if time_accommodation_code:
timed_module = TimedModule(student=request.user, course_id=course_id, module_state_key=location)
timed_module.accommodation_code = time_accommodation_code
timed_module.save()
# Now log the user in:
# user = authenticate(username=testcenteruser.user.username,
# password=testcenteruser.user.password)
#
# if user is None:
# # argh. We couldn't login!
# return HttpResponseRedirect(makeErrorURL(error_url, "ARGH! User cannot log in"));
# UGLY HACK!!!
# Login assumes that authentication has occurred, and that there is a
# backend annotation on the user object, indicating which backend
# against which the user was authenticated. We're authenticating here
# against the registration entry, and assuming that the request given
# this information is correct, we allow the user to be logged in
# without a password. This could all be formalized in a backend object
# that does the above checking.
# TODO: create a backend class to do this.
# testcenteruser.user.backend = "%s.%s" % (backend.__module__, backend.__class__.__name__)
testcenteruser.user.backend = "%s.%s" % ("TestcenterAuthenticationModule", "TestcenterAuthenticationClass")
login(request, testcenteruser.user)
# And start the test:
return redirect(redirect_url)
def _get_news(top=None):
"Return the n top news items on settings.RSS_URL"
......
......@@ -239,6 +239,7 @@ class TimedModule(models.Model):
('ADDHALFTIME', 'Extra Time - 1 1/2 Time'),
('ADD30MIN', 'Extra Time - 30 Minutes'),
('DOUBLE', 'Extra Time - Double Time'),
('TESTING', 'Extra Time -- Large amount for testing purposes')
)
accommodation_code = models.CharField(max_length=12, choices=TIME_ACCOMMODATION_CODES, default='NONE', db_index=True)
......@@ -256,6 +257,9 @@ class TimedModule(models.Model):
return (duration + (30 * 60))
elif self.accommodation_code == 'DOUBLE':
return (duration * 2)
elif self.accommodation_code == 'TESTING':
# when testing, set timer to run for a week at a time.
return 3600 * 24 * 7
# store state:
......
......@@ -415,9 +415,8 @@ def timed_exam(request, course_id, chapter, section):
duration = int(section_descriptor.metadata.get('duration'))
# get corresponding time module, if one is present:
# TODO: determine what to use for module_key...
try:
timed_module = TimedModule.objects.get(student=request.user, course_id=course_id)
timed_module = TimedModule.objects.get(student=request.user, course_id=course_id, module_state_key=section_module.id)
# if a module exists, check to see if it has already been started,
# and if it has already ended.
......@@ -447,8 +446,7 @@ def timed_exam(request, course_id, chapter, section):
except TimedModule.DoesNotExist:
# no entry found. So we're starting this test
# without any accommodations being preset.
# TODO: determine what to use for module_key...
timed_module = TimedModule(student=request.user, course_id=course_id)
timed_module = TimedModule(student=request.user, course_id=course_id, module_state_key=section_module.id)
timed_module.begin(duration)
timed_module.save()
......
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