Commit 76c47305 by Victor Shnayder

whitespace

parent 66934ce2
...@@ -121,8 +121,8 @@ class TestCenterUser(models.Model): ...@@ -121,8 +121,8 @@ class TestCenterUser(models.Model):
The field names and lengths are modeled on the conventions and constraints The field names and lengths are modeled on the conventions and constraints
of Pearson's data import system, including oddities such as suffix having of Pearson's data import system, including oddities such as suffix having
a limit of 255 while last_name only gets 50. a limit of 255 while last_name only gets 50.
Also storing here the confirmation information received from Pearson (if any) Also storing here the confirmation information received from Pearson (if any)
as to the success or failure of the upload. (VCDC file) as to the success or failure of the upload. (VCDC file)
""" """
# Our own record keeping... # Our own record keeping...
...@@ -172,7 +172,7 @@ class TestCenterUser(models.Model): ...@@ -172,7 +172,7 @@ class TestCenterUser(models.Model):
uploaded_at = models.DateTimeField(null=True, blank=True, db_index=True) uploaded_at = models.DateTimeField(null=True, blank=True, db_index=True)
# confirmation back from the test center, as well as timestamps # confirmation back from the test center, as well as timestamps
# on when they processed the request, and when we received # on when they processed the request, and when we received
# confirmation back. # confirmation back.
processed_at = models.DateTimeField(null=True, db_index=True) processed_at = models.DateTimeField(null=True, db_index=True)
upload_status = models.CharField(max_length=20, blank=True, db_index=True) # 'Error' or 'Accepted' upload_status = models.CharField(max_length=20, blank=True, db_index=True) # 'Error' or 'Accepted'
...@@ -186,52 +186,52 @@ class TestCenterUser(models.Model): ...@@ -186,52 +186,52 @@ class TestCenterUser(models.Model):
@property @property
def needs_uploading(self): def needs_uploading(self):
return self.uploaded_at is None or self.uploaded_at < self.user_updated_at return self.uploaded_at is None or self.uploaded_at < self.user_updated_at
@staticmethod @staticmethod
def user_provided_fields(): def user_provided_fields():
return [ 'first_name', 'middle_name', 'last_name', 'suffix', 'salutation', return [ 'first_name', 'middle_name', 'last_name', 'suffix', 'salutation',
'address_1', 'address_2', 'address_3', 'city', 'state', 'postal_code', 'country', 'address_1', 'address_2', 'address_3', 'city', 'state', 'postal_code', 'country',
'phone', 'extension', 'phone_country_code', 'fax', 'fax_country_code', 'company_name'] 'phone', 'extension', 'phone_country_code', 'fax', 'fax_country_code', 'company_name']
@property @property
def email(self): def email(self):
return self.user.email return self.user.email
def needs_update(self, fields): def needs_update(self, fields):
for fieldname in TestCenterUser.user_provided_fields(): for fieldname in TestCenterUser.user_provided_fields():
if fieldname in fields and getattr(self, fieldname) != fields[fieldname]: if fieldname in fields and getattr(self, fieldname) != fields[fieldname]:
return True return True
return False return False
@staticmethod @staticmethod
def _generate_edx_id(prefix): def _generate_edx_id(prefix):
NUM_DIGITS = 12 NUM_DIGITS = 12
return u"{}{:012}".format(prefix, randint(1, 10**NUM_DIGITS-1)) return u"{}{:012}".format(prefix, randint(1, 10**NUM_DIGITS-1))
@staticmethod @staticmethod
def _generate_candidate_id(): def _generate_candidate_id():
return TestCenterUser._generate_edx_id("edX") return TestCenterUser._generate_edx_id("edX")
@classmethod @classmethod
def create(cls, user): def create(cls, user):
testcenter_user = cls(user=user) testcenter_user = cls(user=user)
# testcenter_user.candidate_id remains unset # testcenter_user.candidate_id remains unset
# assign an ID of our own: # assign an ID of our own:
cand_id = cls._generate_candidate_id() cand_id = cls._generate_candidate_id()
while TestCenterUser.objects.filter(client_candidate_id=cand_id).exists(): while TestCenterUser.objects.filter(client_candidate_id=cand_id).exists():
cand_id = cls._generate_candidate_id() cand_id = cls._generate_candidate_id()
testcenter_user.client_candidate_id = cand_id testcenter_user.client_candidate_id = cand_id
return testcenter_user return testcenter_user
@property @property
def is_accepted(self): def is_accepted(self):
return self.upload_status == TEST_CENTER_STATUS_ACCEPTED return self.upload_status == TEST_CENTER_STATUS_ACCEPTED
@property @property
def is_rejected(self): def is_rejected(self):
return self.upload_status == TEST_CENTER_STATUS_ERROR return self.upload_status == TEST_CENTER_STATUS_ERROR
@property @property
def is_pending(self): def is_pending(self):
return not self.is_accepted and not self.is_rejected return not self.is_accepted and not self.is_rejected
...@@ -239,26 +239,26 @@ class TestCenterUser(models.Model): ...@@ -239,26 +239,26 @@ class TestCenterUser(models.Model):
class TestCenterUserForm(ModelForm): class TestCenterUserForm(ModelForm):
class Meta: class Meta:
model = TestCenterUser model = TestCenterUser
fields = ( 'first_name', 'middle_name', 'last_name', 'suffix', 'salutation', fields = ( 'first_name', 'middle_name', 'last_name', 'suffix', 'salutation',
'address_1', 'address_2', 'address_3', 'city', 'state', 'postal_code', 'country', 'address_1', 'address_2', 'address_3', 'city', 'state', 'postal_code', 'country',
'phone', 'extension', 'phone_country_code', 'fax', 'fax_country_code', 'company_name') 'phone', 'extension', 'phone_country_code', 'fax', 'fax_country_code', 'company_name')
def update_and_save(self): def update_and_save(self):
new_user = self.save(commit=False) new_user = self.save(commit=False)
# create additional values here: # create additional values here:
new_user.user_updated_at = datetime.utcnow() new_user.user_updated_at = datetime.utcnow()
new_user.upload_status = '' new_user.upload_status = ''
new_user.save() new_user.save()
log.info("Updated demographic information for user's test center exam registration: username \"{}\" ".format(new_user.user.username)) log.info("Updated demographic information for user's test center exam registration: username \"{}\" ".format(new_user.user.username))
# add validation: # add validation:
def clean_country(self): def clean_country(self):
code = self.cleaned_data['country'] code = self.cleaned_data['country']
if code and len(code) != 3: if code and len(code) != 3:
raise forms.ValidationError(u'Must be three characters (ISO 3166-1): e.g. USA, CAN, MNG') raise forms.ValidationError(u'Must be three characters (ISO 3166-1): e.g. USA, CAN, MNG')
return code return code
def clean(self): def clean(self):
def _can_encode_as_latin(fieldvalue): def _can_encode_as_latin(fieldvalue):
try: try:
...@@ -266,40 +266,40 @@ class TestCenterUserForm(ModelForm): ...@@ -266,40 +266,40 @@ class TestCenterUserForm(ModelForm):
except UnicodeEncodeError: except UnicodeEncodeError:
return False return False
return True return True
cleaned_data = super(TestCenterUserForm, self).clean() cleaned_data = super(TestCenterUserForm, self).clean()
# check for interactions between fields: # check for interactions between fields:
if 'country' in cleaned_data: if 'country' in cleaned_data:
country = cleaned_data.get('country') country = cleaned_data.get('country')
if country == 'USA' or country == 'CAN': if country == 'USA' or country == 'CAN':
if 'state' in cleaned_data and len(cleaned_data['state']) == 0: if 'state' in cleaned_data and len(cleaned_data['state']) == 0:
self._errors['state'] = self.error_class([u'Required if country is USA or CAN.']) self._errors['state'] = self.error_class([u'Required if country is USA or CAN.'])
del cleaned_data['state'] del cleaned_data['state']
if 'postal_code' in cleaned_data and len(cleaned_data['postal_code']) == 0: if 'postal_code' in cleaned_data and len(cleaned_data['postal_code']) == 0:
self._errors['postal_code'] = self.error_class([u'Required if country is USA or CAN.']) self._errors['postal_code'] = self.error_class([u'Required if country is USA or CAN.'])
del cleaned_data['postal_code'] del cleaned_data['postal_code']
if 'fax' in cleaned_data and len(cleaned_data['fax']) > 0 and 'fax_country_code' in cleaned_data and len(cleaned_data['fax_country_code']) == 0: if 'fax' in cleaned_data and len(cleaned_data['fax']) > 0 and 'fax_country_code' in cleaned_data and len(cleaned_data['fax_country_code']) == 0:
self._errors['fax_country_code'] = self.error_class([u'Required if fax is specified.']) self._errors['fax_country_code'] = self.error_class([u'Required if fax is specified.'])
del cleaned_data['fax_country_code'] del cleaned_data['fax_country_code']
# check encoding for all fields: # check encoding for all fields:
cleaned_data_fields = [fieldname for fieldname in cleaned_data] cleaned_data_fields = [fieldname for fieldname in cleaned_data]
for fieldname in cleaned_data_fields: for fieldname in cleaned_data_fields:
if not _can_encode_as_latin(cleaned_data[fieldname]): if not _can_encode_as_latin(cleaned_data[fieldname]):
self._errors[fieldname] = self.error_class([u'Must only use characters in Latin-1 (iso-8859-1) encoding']) self._errors[fieldname] = self.error_class([u'Must only use characters in Latin-1 (iso-8859-1) encoding'])
del cleaned_data[fieldname] del cleaned_data[fieldname]
# Always return the full collection of cleaned data. # Always return the full collection of cleaned data.
return cleaned_data return cleaned_data
# our own code to indicate that a request has been rejected. # our own code to indicate that a request has been rejected.
ACCOMMODATION_REJECTED_CODE = 'NONE' ACCOMMODATION_REJECTED_CODE = 'NONE'
ACCOMMODATION_CODES = ( ACCOMMODATION_CODES = (
(ACCOMMODATION_REJECTED_CODE, 'No Accommodation Granted'), (ACCOMMODATION_REJECTED_CODE, 'No Accommodation Granted'),
('EQPMNT', 'Equipment'), ('EQPMNT', 'Equipment'),
('ET12ET', 'Extra Time - 1/2 Exam Time'), ('ET12ET', 'Extra Time - 1/2 Exam Time'),
('ET30MN', 'Extra Time - 30 Minutes'), ('ET30MN', 'Extra Time - 30 Minutes'),
...@@ -309,11 +309,11 @@ ACCOMMODATION_CODES = ( ...@@ -309,11 +309,11 @@ ACCOMMODATION_CODES = (
('SRRERC', 'Separate Room and Reader/Recorder'), ('SRRERC', 'Separate Room and Reader/Recorder'),
('SRRECR', 'Separate Room and Recorder'), ('SRRECR', 'Separate Room and Recorder'),
('SRSEAN', 'Separate Room and Service Animal'), ('SRSEAN', 'Separate Room and Service Animal'),
('SRSGNR', 'Separate Room and Sign Language Interpreter'), ('SRSGNR', 'Separate Room and Sign Language Interpreter'),
) )
ACCOMMODATION_CODE_DICT = { code : name for (code, name) in ACCOMMODATION_CODES } ACCOMMODATION_CODE_DICT = { code : name for (code, name) in ACCOMMODATION_CODES }
class TestCenterRegistration(models.Model): class TestCenterRegistration(models.Model):
""" """
This is our representation of a user's registration for in-person testing, This is our representation of a user's registration for in-person testing,
...@@ -328,20 +328,20 @@ class TestCenterRegistration(models.Model): ...@@ -328,20 +328,20 @@ class TestCenterRegistration(models.Model):
of Pearson's data import system. of Pearson's data import system.
""" """
# to find an exam registration, we key off of the user and course_id. # 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 # If multiple exams per course are possible, we would also need to add the
# exam_series_code. # exam_series_code.
testcenter_user = models.ForeignKey(TestCenterUser, default=None) testcenter_user = models.ForeignKey(TestCenterUser, default=None)
course_id = models.CharField(max_length=128, db_index=True) course_id = models.CharField(max_length=128, db_index=True)
created_at = models.DateTimeField(auto_now_add=True, db_index=True) created_at = models.DateTimeField(auto_now_add=True, db_index=True)
updated_at = models.DateTimeField(auto_now=True, db_index=True) updated_at = models.DateTimeField(auto_now=True, db_index=True)
# user_updated_at happens only when the user makes a change to their data, # 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 # 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. # 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, # The appointment dates, the exam count, and the accommodation codes can be updated,
# but hopefully this won't happen often. # but hopefully this won't happen often.
user_updated_at = models.DateTimeField(db_index=True) user_updated_at = models.DateTimeField(db_index=True)
# "client_authorization_id" is our unique identifier for the authorization. # "client_authorization_id" is our unique identifier for the authorization.
# This must be present for an update or delete to be sent to Pearson. # 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) client_authorization_id = models.CharField(max_length=20, unique=True, db_index=True)
...@@ -351,10 +351,10 @@ class TestCenterRegistration(models.Model): ...@@ -351,10 +351,10 @@ class TestCenterRegistration(models.Model):
eligibility_appointment_date_last = models.DateField(db_index=True) eligibility_appointment_date_last = models.DateField(db_index=True)
# this is really a list of codes, using an '*' as a delimiter. # this is really a list of codes, using an '*' as a delimiter.
# So it's not a choice list. We use the special value of ACCOMMODATION_REJECTED_CODE # So it's not a choice list. We use the special value of ACCOMMODATION_REJECTED_CODE
# to indicate the rejection of an accommodation request. # to indicate the rejection of an accommodation request.
accommodation_code = models.CharField(max_length=64, blank=True) accommodation_code = models.CharField(max_length=64, blank=True)
# store the original text of the accommodation request. # store the original text of the accommodation request.
accommodation_request = models.CharField(max_length=1024, blank=True, db_index=True) accommodation_request = models.CharField(max_length=1024, blank=True, db_index=True)
...@@ -362,7 +362,7 @@ class TestCenterRegistration(models.Model): ...@@ -362,7 +362,7 @@ class TestCenterRegistration(models.Model):
uploaded_at = models.DateTimeField(null=True, db_index=True) uploaded_at = models.DateTimeField(null=True, db_index=True)
# confirmation back from the test center, as well as timestamps # confirmation back from the test center, as well as timestamps
# on when they processed the request, and when we received # on when they processed the request, and when we received
# confirmation back. # confirmation back.
processed_at = models.DateTimeField(null=True, db_index=True) processed_at = models.DateTimeField(null=True, db_index=True)
upload_status = models.CharField(max_length=20, blank=True, db_index=True) # 'Error' or 'Accepted' upload_status = models.CharField(max_length=20, blank=True, db_index=True) # 'Error' or 'Accepted'
...@@ -372,11 +372,11 @@ class TestCenterRegistration(models.Model): ...@@ -372,11 +372,11 @@ class TestCenterRegistration(models.Model):
# (However, it may never be set if we are always initiating such candidate creation.) # (However, it may never be set if we are always initiating such candidate creation.)
authorization_id = models.IntegerField(null=True, db_index=True) authorization_id = models.IntegerField(null=True, db_index=True)
confirmed_at = models.DateTimeField(null=True, db_index=True) confirmed_at = models.DateTimeField(null=True, db_index=True)
@property @property
def candidate_id(self): def candidate_id(self):
return self.testcenter_user.candidate_id return self.testcenter_user.candidate_id
@property @property
def client_candidate_id(self): def client_candidate_id(self):
return self.testcenter_user.client_candidate_id return self.testcenter_user.client_candidate_id
...@@ -389,20 +389,20 @@ class TestCenterRegistration(models.Model): ...@@ -389,20 +389,20 @@ class TestCenterRegistration(models.Model):
return 'Add' return 'Add'
else: else:
# TODO: decide what to send when we have uploaded an initial version, # TODO: decide what to send when we have uploaded an initial version,
# but have not received confirmation back from that upload. If the # but have not received confirmation back from that upload. If the
# registration here has been changed, then we don't know if this changed # registration here has been changed, then we don't know if this changed
# registration should be submitted as an 'add' or an 'update'. # registration should be submitted as an 'add' or an 'update'.
# #
# If the first registration were lost or in error (e.g. bad code), # If the first registration were lost or in error (e.g. bad code),
# the second should be an "Add". If the first were processed successfully, # the second should be an "Add". If the first were processed successfully,
# then the second should be an "Update". We just don't know.... # then the second should be an "Update". We just don't know....
return 'Update' return 'Update'
@property @property
def exam_authorization_count(self): def exam_authorization_count(self):
# TODO: figure out if this should really go in the database (with a default value). # TODO: figure out if this should really go in the database (with a default value).
return 1 return 1
@classmethod @classmethod
def create(cls, testcenter_user, exam, accommodation_request): def create(cls, testcenter_user, exam, accommodation_request):
registration = cls(testcenter_user = testcenter_user) registration = cls(testcenter_user = testcenter_user)
...@@ -418,7 +418,7 @@ class TestCenterRegistration(models.Model): ...@@ -418,7 +418,7 @@ class TestCenterRegistration(models.Model):
@staticmethod @staticmethod
def _generate_authorization_id(): def _generate_authorization_id():
return TestCenterUser._generate_edx_id("edXexam") return TestCenterUser._generate_edx_id("edXexam")
@staticmethod @staticmethod
def _create_client_authorization_id(): def _create_client_authorization_id():
""" """
...@@ -430,8 +430,8 @@ class TestCenterRegistration(models.Model): ...@@ -430,8 +430,8 @@ class TestCenterRegistration(models.Model):
while TestCenterRegistration.objects.filter(client_authorization_id=auth_id).exists(): while TestCenterRegistration.objects.filter(client_authorization_id=auth_id).exists():
auth_id = TestCenterRegistration._generate_authorization_id() auth_id = TestCenterRegistration._generate_authorization_id()
return auth_id return auth_id
# methods for providing registration status details on registration page: # methods for providing registration status details on registration page:
@property @property
def demographics_is_accepted(self): def demographics_is_accepted(self):
return self.testcenter_user.is_accepted return self.testcenter_user.is_accepted
...@@ -439,7 +439,7 @@ class TestCenterRegistration(models.Model): ...@@ -439,7 +439,7 @@ class TestCenterRegistration(models.Model):
@property @property
def demographics_is_rejected(self): def demographics_is_rejected(self):
return self.testcenter_user.is_rejected return self.testcenter_user.is_rejected
@property @property
def demographics_is_pending(self): def demographics_is_pending(self):
return self.testcenter_user.is_pending return self.testcenter_user.is_pending
...@@ -451,7 +451,7 @@ class TestCenterRegistration(models.Model): ...@@ -451,7 +451,7 @@ class TestCenterRegistration(models.Model):
@property @property
def accommodation_is_rejected(self): def accommodation_is_rejected(self):
return len(self.accommodation_request) > 0 and self.accommodation_code == ACCOMMODATION_REJECTED_CODE return len(self.accommodation_request) > 0 and self.accommodation_code == ACCOMMODATION_REJECTED_CODE
@property @property
def accommodation_is_pending(self): def accommodation_is_pending(self):
return len(self.accommodation_request) > 0 and len(self.accommodation_code) == 0 return len(self.accommodation_request) > 0 and len(self.accommodation_code) == 0
...@@ -463,20 +463,20 @@ class TestCenterRegistration(models.Model): ...@@ -463,20 +463,20 @@ class TestCenterRegistration(models.Model):
@property @property
def registration_is_accepted(self): def registration_is_accepted(self):
return self.upload_status == TEST_CENTER_STATUS_ACCEPTED return self.upload_status == TEST_CENTER_STATUS_ACCEPTED
@property @property
def registration_is_rejected(self): def registration_is_rejected(self):
return self.upload_status == TEST_CENTER_STATUS_ERROR return self.upload_status == TEST_CENTER_STATUS_ERROR
@property @property
def registration_is_pending(self): def registration_is_pending(self):
return not self.registration_is_accepted and not self.registration_is_rejected return not self.registration_is_accepted and not self.registration_is_rejected
# methods for providing registration status summary on dashboard page: # methods for providing registration status summary on dashboard page:
@property @property
def is_accepted(self): def is_accepted(self):
return self.registration_is_accepted and self.demographics_is_accepted return self.registration_is_accepted and self.demographics_is_accepted
@property @property
def is_rejected(self): def is_rejected(self):
return self.registration_is_rejected or self.demographics_is_rejected return self.registration_is_rejected or self.demographics_is_rejected
...@@ -484,17 +484,17 @@ class TestCenterRegistration(models.Model): ...@@ -484,17 +484,17 @@ class TestCenterRegistration(models.Model):
@property @property
def is_pending(self): def is_pending(self):
return not self.is_accepted and not self.is_rejected return not self.is_accepted and not self.is_rejected
def get_accommodation_codes(self): def get_accommodation_codes(self):
return self.accommodation_code.split('*') return self.accommodation_code.split('*')
def get_accommodation_names(self): def get_accommodation_names(self):
return [ ACCOMMODATION_CODE_DICT.get(code, "Unknown code " + code) for code in self.get_accommodation_codes() ] return [ ACCOMMODATION_CODE_DICT.get(code, "Unknown code " + code) for code in self.get_accommodation_codes() ]
@property @property
def registration_signup_url(self): def registration_signup_url(self):
return settings.PEARSONVUE_SIGNINPAGE_URL return settings.PEARSONVUE_SIGNINPAGE_URL
class TestCenterRegistrationForm(ModelForm): class TestCenterRegistrationForm(ModelForm):
class Meta: class Meta:
model = TestCenterRegistration model = TestCenterRegistration
...@@ -505,33 +505,33 @@ class TestCenterRegistrationForm(ModelForm): ...@@ -505,33 +505,33 @@ class TestCenterRegistrationForm(ModelForm):
if code and len(code) > 0: if code and len(code) > 0:
return code.strip() return code.strip()
return code return code
def update_and_save(self): def update_and_save(self):
registration = self.save(commit=False) registration = self.save(commit=False)
# create additional values here: # create additional values here:
registration.user_updated_at = datetime.utcnow() registration.user_updated_at = datetime.utcnow()
registration.upload_status = '' registration.upload_status = ''
registration.save() registration.save()
log.info("Updated registration information for user's test center exam registration: username \"{}\" course \"{}\", examcode \"{}\"".format(registration.testcenter_user.user.username, registration.course_id, registration.exam_series_code)) log.info("Updated registration information for user's test center exam registration: username \"{}\" course \"{}\", examcode \"{}\"".format(registration.testcenter_user.user.username, registration.course_id, registration.exam_series_code))
# TODO: add validation code for values added to accommodation_code field. # TODO: add validation code for values added to accommodation_code field.
def get_testcenter_registration(user, course_id, exam_series_code): def get_testcenter_registration(user, course_id, exam_series_code):
try: try:
tcu = TestCenterUser.objects.get(user=user) tcu = TestCenterUser.objects.get(user=user)
except TestCenterUser.DoesNotExist: except TestCenterUser.DoesNotExist:
return [] return []
return TestCenterRegistration.objects.filter(testcenter_user=tcu, course_id=course_id, exam_series_code=exam_series_code) return TestCenterRegistration.objects.filter(testcenter_user=tcu, course_id=course_id, exam_series_code=exam_series_code)
def unique_id_for_user(user): def unique_id_for_user(user):
""" """
Return a unique id for a user, suitable for inserting into Return a unique id for a user, suitable for inserting into
e.g. personalized survey links. e.g. personalized survey links.
""" """
# include the secret key as a salt, and to make the ids unique across # include the secret key as a salt, and to make the ids unique across
# different LMS installs. # different LMS installs.
h = hashlib.md5() h = hashlib.md5()
h.update(settings.SECRET_KEY) h.update(settings.SECRET_KEY)
h.update(str(user.id)) h.update(str(user.id))
......
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