Skip to content
Snippets Groups Projects
views.py 49 KiB
Newer Older
    return HttpResponse(json.dumps({'success': True}))
@ensure_csrf_cookie
def pending_name_changes(request):
Piotr Mitros's avatar
Piotr Mitros committed
    ''' Web page which allows staff to approve or reject name changes. '''
    if not request.user.is_staff:
        raise Http404

    changes = list(PendingNameChange.objects.all())
    js = {'students': [{'new_name': c.new_name,
Calen Pennington's avatar
Calen Pennington committed
                        'rationale': c.rationale,
                        'old_name': UserProfile.objects.get(user=c.user).name,
                        'email': c.user.email,
                        'uid': c.user.id,
                        'cid': c.id} for c in changes]}
Matthew Mongeau's avatar
Matthew Mongeau committed
    return render_to_response('name_changes.html', js)
@ensure_csrf_cookie
def reject_name_change(request):
Piotr Mitros's avatar
Piotr Mitros committed
    ''' JSON: Name change process. Course staff clicks 'reject' on a given name change '''
    if not request.user.is_staff:
        raise Http404

Matthew Mongeau's avatar
Matthew Mongeau committed
    try:
        pnc = PendingNameChange.objects.get(id=int(request.POST['id']))
Matthew Mongeau's avatar
Matthew Mongeau committed
    except PendingNameChange.DoesNotExist:
        return HttpResponse(json.dumps({'success': False, 'error': 'Invalid ID'}))
    pnc.delete()
    return HttpResponse(json.dumps({'success': True}))

def accept_name_change_by_id(id):
Matthew Mongeau's avatar
Matthew Mongeau committed
    try:
        pnc = PendingNameChange.objects.get(id=id)
Matthew Mongeau's avatar
Matthew Mongeau committed
    except PendingNameChange.DoesNotExist:
        return HttpResponse(json.dumps({'success': False, 'error': 'Invalid ID'}))

    u = pnc.user
    up = UserProfile.objects.get(user=u)

    # Save old name
    meta = up.get_meta()
    if 'old_names' not in meta:
        meta['old_names'] = []
    meta['old_names'].append([up.name, pnc.rationale, datetime.datetime.now().isoformat()])
    up.set_meta(meta)

    up.name = pnc.new_name
    up.save()
    pnc.delete()

    return HttpResponse(json.dumps({'success': True}))


@ensure_csrf_cookie
def accept_name_change(request):
Victor Shnayder's avatar
Victor Shnayder committed
    ''' JSON: Name change process. Course staff clicks 'accept' on a given name change

    We used this during the prototype but now we simply record name changes instead
    of manually approving them. Still keeping this around in case we want to go
    back to this approval method.
    '''
    if not request.user.is_staff:
        raise Http404

    return accept_name_change_by_id(int(request.POST['id']))
Brian Wilson's avatar
Brian Wilson committed
@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):
        log.error("generating error URL with error code {}".format(error_code))
        return "{}?code={}".format(error_url, error_code);
Brian Wilson's avatar
Brian Wilson committed
     
    # get provided error URL, which will be used as a known prefix for returning error messages to the
Brian Wilson's avatar
Brian Wilson committed
    error_url = request.POST.get("errorURL")

    # TODO: check that the parameters have not been tampered with, by comparing the code provided by Pearson
Brian Wilson's avatar
Brian Wilson committed
    # 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:
        log.error("not able to find demographics for cand ID {}".format(client_candidate_id))
Brian Wilson's avatar
Brian Wilson committed
        return HttpResponseRedirect(makeErrorURL(error_url, "invalidClientCandidateID"));

    # find testcenter_registration that matches the provided exam code:
    # Note that we could rely in future on either the registrationId or the exam code, 
    # or possibly both.  But for now we know what to do with an ExamSeriesCode, 
    # while we currently have no record of RegistrationID values at all.
Brian Wilson's avatar
Brian Wilson committed
    if 'vueExamSeriesCode' not in request.POST:
        # we are not allowed to make up a new error code, according to Pearson, 
        # so instead of "missingExamSeriesCode", we use a valid one that is 
        # inaccurate but at least distinct.  (Sigh.)
        log.error("missing exam series code for cand ID {}".format(client_candidate_id))
        return HttpResponseRedirect(makeErrorURL(error_url, "missingPartnerID"));
Brian Wilson's avatar
Brian Wilson committed
    exam_series_code = request.POST.get('vueExamSeriesCode')
    # special case for supporting test user:
    if client_candidate_id == "edX003671291147" and exam_series_code != '6002x001':
        log.warning("test user {} using unexpected exam code {}, coercing to 6002x001".format(client_candidate_id, exam_series_code))
        exam_series_code = '6002x001'

Brian Wilson's avatar
Brian Wilson committed
    registrations = TestCenterRegistration.objects.filter(testcenter_user=testcenteruser, exam_series_code=exam_series_code)
    if not registrations:
        log.error("not able to find exam registration for exam {} and cand ID {}".format(exam_series_code, client_candidate_id))
Brian Wilson's avatar
Brian Wilson committed
        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
    course = course_from_id(course_id)  # assume it will be found....
    if not course:
        log.error("not able to find course from ID {} for cand ID {}".format(course_id, client_candidate_id))
Brian Wilson's avatar
Brian Wilson committed
        return HttpResponseRedirect(makeErrorURL(error_url, "incorrectCandidateTests"));
    exam = course.get_test_center_exam(exam_series_code)
    if not exam:
        log.error("not able to find exam {} for course ID {} and cand ID {}".format(exam_series_code, course_id, client_candidate_id))
        return HttpResponseRedirect(makeErrorURL(error_url, "incorrectCandidateTests"));
    location = exam.exam_url
    log.info("proceeding with test of cand {} on exam {} for course {}: URL = {}".format(client_candidate_id, exam_series_code, course_id, location))

    # check if the test has already been taken
    timelimit_descriptor = modulestore().get_instance(course_id, Location(location))
    if not timelimit_descriptor:
        log.error("cand {} on exam {} for course {}: descriptor not found for location {}".format(client_candidate_id, exam_series_code, course_id, location))
        return HttpResponseRedirect(makeErrorURL(error_url, "missingClientProgram"));
        
    timelimit_module_cache = StudentModuleCache.cache_for_descriptor_descendents(course_id, testcenteruser.user, 
                                                                                 timelimit_descriptor, depth=None)
    timelimit_module = get_module_for_descriptor(request.user, request, timelimit_descriptor, 
                                                 timelimit_module_cache, course_id, position=None)
    if not timelimit_module.category == 'timelimit':
        log.error("cand {} on exam {} for course {}: non-timelimit module at location {}".format(client_candidate_id, exam_series_code, course_id, location))
        return HttpResponseRedirect(makeErrorURL(error_url, "missingClientProgram"));
        
    if timelimit_module and timelimit_module.has_ended:
        log.warning("cand {} on exam {} for course {}: test already over at {}".format(client_candidate_id, exam_series_code, course_id, timelimit_module.ending_at))
        return HttpResponseRedirect(makeErrorURL(error_url, "allTestsTaken"));
    # check if we need to provide an accommodation:
Brian Wilson's avatar
Brian Wilson committed
    time_accommodation_mapping = {'ET12ET' : 'ADDHALFTIME',
                                  'ET30MN' : 'ADD30MIN',
                                  'ETDBTM' : 'ADDDOUBLE', }

    time_accommodation_code = None
Brian Wilson's avatar
Brian Wilson committed
    for code in registration.get_accommodation_codes():
        if code in time_accommodation_mapping:
            time_accommodation_code = time_accommodation_mapping[code]
    # special, hard-coded client ID used by Pearson shell for testing:
    if client_candidate_id == "edX003671291147":
        time_accommodation_code = 'TESTING'
        
    if time_accommodation_code:
        timelimit_module.accommodation_code = time_accommodation_code
        instance_module = get_instance_module(course_id, testcenteruser.user, timelimit_module, timelimit_module_cache)
        instance_module.state = timelimit_module.get_instance_state()
        instance_module.save()
        log.info("cand {} on exam {} for course {}: receiving accommodation {}".format(client_candidate_id, exam_series_code, course_id, time_accommodation_code))
Brian Wilson's avatar
Brian Wilson committed
        
    # 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: (brian) create a backend class to do this.
Brian Wilson's avatar
Brian Wilson committed
    # 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 jump_to(request, course_id, location)
def _get_news(top=None):
    "Return the n top news items on settings.RSS_URL"

    feed_data = cache.get("students_index_rss_feed_data")
    if feed_data == None:
        if hasattr(settings, 'RSS_URL'):
            feed_data = urllib.urlopen(settings.RSS_URL).read()
        else:
            feed_data = render_to_string("feed.rss", None)
        cache.set("students_index_rss_feed_data", feed_data, settings.RSS_TIMEOUT)

    feed = feedparser.parse(feed_data)
    entries = feed['entries'][0:top]  # all entries if top is None
    for entry in entries:
        soup = BeautifulSoup(entry.description)
        entry.image = soup.img['src'] if soup.img else None
        entry.summary = soup.getText()

    return entries