From ee99080687a8f9b7312bab14e02d6a4ea4de7a7a Mon Sep 17 00:00:00 2001 From: Brian Wilson Date: Wed, 19 Dec 2012 16:53:13 -0500 Subject: [PATCH] add additional fields to testcenter user and update test center registration. --- ...022_add_more_fields_to_test_center_user.py | 198 ++++++++++++++++++ common/djangoapps/student/models.py | 50 ++++- common/djangoapps/student/views.py | 75 ++++--- common/lib/xmodule/xmodule/course_module.py | 15 +- lms/templates/dashboard.html | 20 +- lms/templates/test_center_register.html | 7 +- 6 files changed, 314 insertions(+), 51 deletions(-) create mode 100644 common/djangoapps/student/migrations/0022_add_more_fields_to_test_center_user.py diff --git a/common/djangoapps/student/migrations/0022_add_more_fields_to_test_center_user.py b/common/djangoapps/student/migrations/0022_add_more_fields_to_test_center_user.py new file mode 100644 index 0000000000..4a6481f80c --- /dev/null +++ b/common/djangoapps/student/migrations/0022_add_more_fields_to_test_center_user.py @@ -0,0 +1,198 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding field 'TestCenterUser.upload_status' + db.add_column('student_testcenteruser', 'upload_status', + self.gf('django.db.models.fields.CharField')(default='', max_length=20, blank=True), + keep_default=False) + + # Adding field 'TestCenterUser.confirmed_at' + db.add_column('student_testcenteruser', 'confirmed_at', + self.gf('django.db.models.fields.DateTimeField')(null=True, db_index=True), + keep_default=False) + + # Adding field 'TestCenterUser.upload_error_message' + db.add_column('student_testcenteruser', 'upload_error_message', + self.gf('django.db.models.fields.CharField')(default='', max_length=512, blank=True), + keep_default=False) + + # Adding model 'TestCenterRegistration' + db.create_table('student_testcenterregistration', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('testcenter_user', self.gf('django.db.models.fields.related.ForeignKey')(default=None, to=orm['student.TestCenterUser'], unique=True)), + ('course_id', self.gf('django.db.models.fields.CharField')(max_length=128, db_index=True)), + ('created_at', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, db_index=True, blank=True)), + ('updated_at', self.gf('django.db.models.fields.DateTimeField')(auto_now=True, db_index=True, blank=True)), + ('user_updated_at', self.gf('django.db.models.fields.DateTimeField')(db_index=True)), + ('client_authorization_id', self.gf('django.db.models.fields.CharField')(unique=True, max_length=20, db_index=True)), + ('exam_series_code', self.gf('django.db.models.fields.CharField')(max_length=15, db_index=True)), + ('eligibility_appointment_date_first', self.gf('django.db.models.fields.DateField')(db_index=True)), + ('eligibility_appointment_date_last', self.gf('django.db.models.fields.DateField')(db_index=True)), + ('accommodation_code', self.gf('django.db.models.fields.CharField')(max_length=64, blank=True)), + ('accommodation_request', self.gf('django.db.models.fields.CharField')(max_length=1024, blank=True)), + ('upload_status', self.gf('django.db.models.fields.CharField')(max_length=20, blank=True)), + ('confirmed_at', self.gf('django.db.models.fields.DateTimeField')(db_index=True)), + ('upload_error_message', self.gf('django.db.models.fields.CharField')(max_length=512, blank=True)), + )) + db.send_create_signal('student', ['TestCenterRegistration']) + + + def backwards(self, orm): + # Deleting model 'TestCenterRegistration' + db.delete_table('student_testcenterregistration') + + # Deleting field 'TestCenterUser.upload_status' + db.delete_column('student_testcenteruser', 'upload_status') + + # Deleting field 'TestCenterUser.confirmed_at' + db.delete_column('student_testcenteruser', 'confirmed_at') + + # Deleting field 'TestCenterUser.upload_error_message' + db.delete_column('student_testcenteruser', 'upload_error_message') + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'student.courseenrollment': { + 'Meta': {'unique_together': "(('user', 'course_id'),)", 'object_name': 'CourseEnrollment'}, + 'course_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'null': 'True', 'db_index': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) + }, + 'student.pendingemailchange': { + 'Meta': {'object_name': 'PendingEmailChange'}, + 'activation_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32', 'db_index': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'new_email': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True'}) + }, + 'student.pendingnamechange': { + 'Meta': {'object_name': 'PendingNameChange'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'new_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'rationale': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True'}) + }, + 'student.registration': { + 'Meta': {'object_name': 'Registration', 'db_table': "'auth_registration'"}, + 'activation_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32', 'db_index': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'unique': 'True'}) + }, + 'student.testcenterregistration': { + 'Meta': {'object_name': 'TestCenterRegistration'}, + 'accommodation_code': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}), + 'accommodation_request': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}), + 'client_authorization_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20', 'db_index': 'True'}), + 'confirmed_at': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}), + 'course_id': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), + 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}), + 'eligibility_appointment_date_first': ('django.db.models.fields.DateField', [], {'db_index': 'True'}), + 'eligibility_appointment_date_last': ('django.db.models.fields.DateField', [], {'db_index': 'True'}), + 'exam_series_code': ('django.db.models.fields.CharField', [], {'max_length': '15', 'db_index': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'testcenter_user': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['student.TestCenterUser']", 'unique': 'True'}), + 'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}), + 'upload_error_message': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}), + 'upload_status': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), + 'user_updated_at': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}) + }, + 'student.testcenteruser': { + 'Meta': {'object_name': 'TestCenterUser'}, + 'address_1': ('django.db.models.fields.CharField', [], {'max_length': '40'}), + 'address_2': ('django.db.models.fields.CharField', [], {'max_length': '40', 'blank': 'True'}), + 'address_3': ('django.db.models.fields.CharField', [], {'max_length': '40', 'blank': 'True'}), + 'candidate_id': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'db_index': 'True'}), + 'city': ('django.db.models.fields.CharField', [], {'max_length': '32', 'db_index': 'True'}), + 'client_candidate_id': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}), + 'company_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), + 'confirmed_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'db_index': 'True'}), + 'country': ('django.db.models.fields.CharField', [], {'max_length': '3', 'db_index': 'True'}), + 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}), + 'extension': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '8', 'blank': 'True'}), + 'fax': ('django.db.models.fields.CharField', [], {'max_length': '35', 'blank': 'True'}), + 'fax_country_code': ('django.db.models.fields.CharField', [], {'max_length': '3', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'db_index': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}), + 'middle_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'phone': ('django.db.models.fields.CharField', [], {'max_length': '35'}), + 'phone_country_code': ('django.db.models.fields.CharField', [], {'max_length': '3', 'db_index': 'True'}), + 'postal_code': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '16', 'blank': 'True'}), + 'salutation': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), + 'state': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '20', 'blank': 'True'}), + 'suffix': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}), + 'upload_error_message': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}), + 'upload_status': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['auth.User']", 'unique': 'True'}), + 'user_updated_at': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}) + }, + 'student.userprofile': { + 'Meta': {'object_name': 'UserProfile', 'db_table': "'auth_userprofile'"}, + 'courseware': ('django.db.models.fields.CharField', [], {'default': "'course.xml'", 'max_length': '255', 'blank': 'True'}), + 'gender': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '6', 'null': 'True', 'blank': 'True'}), + 'goals': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'language': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255', 'blank': 'True'}), + 'level_of_education': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '6', 'null': 'True', 'blank': 'True'}), + 'location': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255', 'blank': 'True'}), + 'mailing_address': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'meta': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'profile'", 'unique': 'True', 'to': "orm['auth.User']"}), + 'year_of_birth': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}) + }, + 'student.usertestgroup': { + 'Meta': {'object_name': 'UserTestGroup'}, + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '32', 'db_index': 'True'}), + 'users': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.User']", 'db_index': 'True', 'symmetrical': 'False'}) + } + } + + complete_apps = ['student'] diff --git a/common/djangoapps/student/models.py b/common/djangoapps/student/models.py index b7b2b4e21d..6b73c2319f 100644 --- a/common/djangoapps/student/models.py +++ b/common/djangoapps/student/models.py @@ -141,6 +141,9 @@ class TestCenterUser(models.Model): The field names and lengths are modeled on the conventions and constraints of Pearson's data import system, including oddities such as suffix having a limit of 255 while last_name only gets 50. + + Also storing here the confirmation information received from Pearson (if any) + as to the success or failure of the upload. (VCDC file) """ # Our own record keeping... user = models.ForeignKey(User, unique=True, default=None) @@ -155,7 +158,7 @@ class TestCenterUser(models.Model): # we first create the User entry, and is assigned by Pearson later. candidate_id = models.IntegerField(null=True, db_index=True) - # Unique ID we assign our user for a the Test Center. + # Unique ID we assign our user for the Test Center. client_candidate_id = models.CharField(max_length=50, db_index=True) # Name @@ -189,6 +192,11 @@ class TestCenterUser(models.Model): # Company company_name = models.CharField(max_length=50, blank=True) + # Confirmation + upload_status = models.CharField(max_length=20, blank=True) # 'Error' or 'Accepted' + confirmed_at = models.DateTimeField(null=True, db_index=True) + upload_error_message = models.CharField(max_length=512, blank=True) + @staticmethod def user_provided_fields(): return [ 'first_name', 'middle_name', 'last_name', 'suffix', 'salutation', @@ -212,17 +220,38 @@ class TestCenterRegistration(models.Model): The field names and lengths are modeled on the conventions and constraints of Pearson's data import system. """ - # TODO: Check the spec to find out lengths specified by Pearson - + # to find an exam registration, we key off of the user and course_id. + # If multiple exams per course are possible, we would also need to add the + # exam_series_code. testcenter_user = models.ForeignKey(TestCenterUser, unique=True, default=None) + course_id = models.CharField(max_length=128, db_index=True) + created_at = models.DateTimeField(auto_now_add=True, db_index=True) updated_at = models.DateTimeField(auto_now=True, db_index=True) - course_id = models.CharField(max_length=128, db_index=True) + # user_updated_at happens only when the user makes a change to their data, + # and is something Pearson needs to know to manage updates. Unlike + # updated_at, this will not get incremented when we do a batch data import. + # The appointment dates, the exam count, and the accommodation codes can be updated, + # but hopefully this won't happen often. + user_updated_at = models.DateTimeField(db_index=True) + # "client_authorization_id" is the client's unique identifier for the authorization. + # This must be present for an update or delete to be sent to Pearson. + client_authorization_id = models.CharField(max_length=20, unique=True, db_index=True) + + # information about the test, from the course policy: + exam_series_code = models.CharField(max_length=15, db_index=True) + eligibility_appointment_date_first = models.DateField(db_index=True) + eligibility_appointment_date_last = models.DateField(db_index=True) + # TODO: this should be an enumeration: + accommodation_code = models.CharField(max_length=64, blank=True) # store the original text of the accommodation request. accommodation_request = models.CharField(max_length=1024, blank=True) - # TODO: this should be an enumeration: - accommodation_code = models.CharField(max_length=64, blank=True) + + # Confirmation + upload_status = models.CharField(max_length=20, blank=True) # 'Error' or 'Accepted' + confirmed_at = models.DateTimeField(db_index=True) + upload_error_message = models.CharField(max_length=512, blank=True) @property def candidate_id(self): @@ -232,6 +261,15 @@ class TestCenterRegistration(models.Model): def client_candidate_id(self): return self.testcenter_user.client_candidate_id + + +def get_testcenter_registrations_for_user_and_course(user, course_id): + try: + tcu = TestCenterUser.objects.get(user=user) + except User.DoesNotExist: + return [] + return TestCenterRegistration.objects.filter(testcenter_user=tcu, course_id=course_id) + def unique_id_for_user(user): """ Return a unique id for a user, suitable for inserting into diff --git a/common/djangoapps/student/views.py b/common/djangoapps/student/views.py index cfb6b3a39b..4d5728792e 100644 --- a/common/djangoapps/student/views.py +++ b/common/djangoapps/student/views.py @@ -1,12 +1,12 @@ import datetime import feedparser -import itertools +#import itertools import json import logging import random import string import sys -import time +#import time import urllib import uuid @@ -19,7 +19,8 @@ from django.core.context_processors import csrf from django.core.mail import send_mail 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 @@ -37,13 +38,14 @@ from xmodule.modulestore.exceptions import ItemNotFoundError from xmodule.modulestore.django import modulestore from xmodule.modulestore.exceptions import ItemNotFoundError -from datetime import date +#from datetime import date from collections import namedtuple from courseware.courses import get_courses_by_university from courseware.access import has_access from statsd import statsd +from django.contrib.localflavor.ie.ie_counties import IE_COUNTY_CHOICES log = logging.getLogger("mitx.student") Article = namedtuple('Article', 'title url author image deck publication publish_date') @@ -640,9 +642,11 @@ def _do_create_or_update_test_center_user(post_vars): # first determine if we need to create a new TestCenterUser, or if we are making any update # to an existing TestCenterUser. - username=post_vars['username'] + username = post_vars['username'] user = User.objects.get(username=username) - + course_id = post_vars['course_id'] + course = (course_from_id(course_id)) # assume it will be found.... + needs_saving = False try: testcenter_user = TestCenterUser.objects.get(user=user) @@ -651,9 +655,8 @@ def _do_create_or_update_test_center_user(post_vars): for fieldname in TestCenterUser.user_provided_fields()]) if needs_updating: - # TODO: what do we set a timestamp to, in order to get now()? + # leave user and client_candidate_id as before testcenter_user.user_updated_at = datetime.datetime.now() - # Now do the update: for fieldname in TestCenterUser.user_provided_fields(): testcenter_user.__setattr__(fieldname, post_vars[fieldname]) needs_saving = True @@ -661,7 +664,6 @@ def _do_create_or_update_test_center_user(post_vars): except TestCenterUser.DoesNotExist: # did not find the TestCenterUser, so create a new one testcenter_user = TestCenterUser(user=user) - # testcenter_user.user = user for fieldname in TestCenterUser.user_provided_fields(): testcenter_user.__setattr__(fieldname, post_vars[fieldname]) # testcenter_user.candidate_id remains unset @@ -670,31 +672,43 @@ def _do_create_or_update_test_center_user(post_vars): needs_saving = True # additional validation occurs at save time, so handle exceptions - # TODO: Rearrange so that if part of the process fails, the whole process fails. - # Right now, we can have e.g. no registration e-mail sent out and a zombie account if needs_saving: try: testcenter_user.save() - except IntegrityError: - js = {'success': False} - # TODO: Figure out the cause of the integrity error - if len(User.objects.filter(username=post_vars['username'])) > 0: - js['value'] = "An account with this username already exists." - js['field'] = 'username' - return HttpResponse(json.dumps(js)) + except IntegrityError, ie: + message = ie + context = {'course': course, + 'user': user, + 'message': message, + 'testcenteruser': testcenter_user, + } + return render_to_response('test_center_register.html', context) - if len(User.objects.filter(email=post_vars['email'])) > 0: - js['value'] = "An account with this e-mail already exists." - js['field'] = 'email' - return HttpResponse(json.dumps(js)) - - raise - - + # create and save the registration: registration = TestCenterRegistration(testcenter_user = testcenter_user) - # registration.register(user) registration.course_id = post_vars['course_id'] registration.accommodation_request = post_vars['accommodations'] + exam_info = course.testcenter_info + registration.exam_series_code = exam_info.get('Exam_Series_Code') + registration.eligibility_appointment_date_first = exam_info.get('First_Eligible_Appointment_Date') + registration.eligibility_appointment_date_last = exam_info.get('Last_Eligible_Appointment_Date') + # accommodation_code remains blank for now, along with Pearson confirmation + registration.user_updated_at = datetime.datetime.now() + + # "client_authorization_id" is the client's unique identifier for the authorization. + # This must be present for an update or delete to be sent to Pearson. + registration.client_authorization_id = "1" + try: + registration.save() + except IntegrityError, ie: + message = ie + context = {'course': course, + 'user': user, + 'message': message, + 'testcenteruser': testcenter_user, + } + return render_to_response('test_center_register.html', context) + return (user, testcenter_user, registration) @@ -759,11 +773,12 @@ def create_test_registration(request, post_override=None): js['value'] = 'Could not send accommodation e-mail.' return HttpResponse(json.dumps(js)) - + # TODO: enable appropriate stat # statsd.increment("common.student.account_created") - js = {'success': True} - return HttpResponse(json.dumps(js), mimetype="application/json") +# js = {'success': True} +# return HttpResponse(json.dumps(js), mimetype="application/json") + return HttpResponseRedirect('/dashboard') def get_random_post_override(): """ diff --git a/common/lib/xmodule/xmodule/course_module.py b/common/lib/xmodule/xmodule/course_module.py index a4cf87b333..715b263b59 100644 --- a/common/lib/xmodule/xmodule/course_module.py +++ b/common/lib/xmodule/xmodule/course_module.py @@ -315,7 +315,7 @@ class CourseDescriptor(SequenceDescriptor): Returns None if no url specified. """ return self.metadata.get('end_of_course_survey_url') - + @property def testcenter_info(self): """ @@ -324,10 +324,17 @@ class CourseDescriptor(SequenceDescriptor): TODO: decide if we expect this entry to be a single test, or if multiple tests are possible per course. - Returns None if no testcenter info specified. + For now we expect this entry to be a single test. + + Returns None if no testcenter info specified, or if no exam is included. """ - return self.metadata.get('testcenter_info') - + info = self.metadata.get('testcenter_info') + if info is None or len(info) == 0: + return None; + else: + return info.values()[0] + + @property def title(self): return self.display_name diff --git a/lms/templates/dashboard.html b/lms/templates/dashboard.html index c7a8df077a..13e393180a 100644 --- a/lms/templates/dashboard.html +++ b/lms/templates/dashboard.html @@ -3,6 +3,7 @@ from courseware.courses import course_image_url, get_course_about_section from courseware.access import has_access from certificates.models import CertificateStatuses + from student.models import get_testcenter_registrations_for_user_and_course %> <%inherit file="main.html" /> @@ -222,20 +223,26 @@ <% testcenter_info = course.testcenter_info + testcenter_register_target = reverse('begin_test_registration', args=[course.id]) %> % if testcenter_info is not None: - - <% - testcenter_register_target = reverse('begin_test_registration', args=[course.id]) - %> + + + <% + registrations = get_testcenter_registrations_for_user_and_course(user, course.id) + %> + % if len(registrations) == 0:
Register for Pearson exam

Registration for the Pearson exam is now open.

- + % else:
-

Your registration for the Pearson exam is pending. Within a few days, you should receive a confirmation number, which can be used to schedule your exam.

+

Your + registration for the Pearson exam + is pending. Within a few days, you should see a confirmation number here, which can be used to schedule your exam.

@@ -244,6 +251,7 @@

Registration number: edx00015879548

Write this down! You’ll need it to schedule your exam.

+ % endif % endif diff --git a/lms/templates/test_center_register.html b/lms/templates/test_center_register.html index c71c40610d..51b271dfc3 100644 --- a/lms/templates/test_center_register.html +++ b/lms/templates/test_center_register.html @@ -71,12 +71,9 @@ <% - testcenter_info = course.testcenter_info + exam_info = course.testcenter_info %> - % if testcenter_info is not None: - <% - exam_info = testcenter_info.get('Final_Exam') - %> + % if exam_info is not None:

Exam Series Code: ${exam_info.get('Exam_Series_Code')}

First Eligible Appointment Date: ${exam_info.get('First_Eligible_Appointment_Date')}

Last Eligible Appointment Date: ${exam_info.get('Last_Eligible_Appointment_Date')}