diff --git a/common/djangoapps/student/management/commands/pearson_dump.py b/common/djangoapps/student/management/commands/pearson_dump.py new file mode 100644 index 0000000000..28931dc468 --- /dev/null +++ b/common/djangoapps/student/management/commands/pearson_dump.py @@ -0,0 +1,76 @@ +from optparse import make_option +from json import dump + +from django.core.management.base import BaseCommand, CommandError + +from student.models import TestCenterRegistration + + +class Command(BaseCommand): + + args = '' + help = """ + Dump information as JSON from TestCenterRegistration tables, including username and status. + """ + + option_list = BaseCommand.option_list + ( + make_option('--course_id', + action='store', + dest='course_id', + help='Specify a particular course.'), + make_option('--exam_series_code', + action='store', + dest='exam_series_code', + default=None, + help='Specify a particular exam, using the Pearson code'), + make_option('--accommodation_pending', + action='store_true', + dest='accommodation_pending', + default=False, + ), + ) + + def handle(self, *args, **options): + if len(args) < 1: + raise CommandError("Missing single argument: output JSON file") + + # get output location: + outputfile = args[0] + + # construct the query object to dump: + registrations = TestCenterRegistration.objects.all() + if 'course_id' in options and options['course_id']: + registrations = registrations.filter(course_id=options['course_id']) + if 'exam_series_code' in options and options['exam_series_code']: + registrations = registrations.filter(exam_series_code=options['exam_series_code']) + + # collect output: + output = [] + for registration in registrations: + if 'accommodation_pending' in options and options['accommodation_pending'] and not registration.accommodation_is_pending: + continue + record = {'username' : registration.testcenter_user.user.username, + 'email' : registration.testcenter_user.email, + 'first_name' : registration.testcenter_user.first_name, + 'last_name' : registration.testcenter_user.last_name, + 'client_candidate_id' : registration.client_candidate_id, + 'client_authorization_id' : registration.client_authorization_id, + 'course_id' : registration.course_id, + 'exam_series_code' : registration.exam_series_code, + 'accommodation_request' : registration.accommodation_request, + 'accommodation_code' : registration.accommodation_code, + 'registration_status' : registration.registration_status(), + 'demographics_status' : registration.demographics_status(), + 'accommodation_status' : registration.accommodation_status(), + } + if len(registration.upload_error_message) > 0: + record['registration_error'] = registration.upload_error_message + if registration.needs_uploading: + record['needs_uploading'] = True + + output.append(record) + + # dump output: + with open(outputfile, 'w') as outfile: + dump(output, outfile) + diff --git a/common/djangoapps/student/management/commands/pearson_import_conf_zip.py b/common/djangoapps/student/management/commands/pearson_import_conf_zip.py index 9c3a34a90c..d94c3ba863 100644 --- a/common/djangoapps/student/management/commands/pearson_import_conf_zip.py +++ b/common/djangoapps/student/management/commands/pearson_import_conf_zip.py @@ -65,7 +65,7 @@ class Command(BaseCommand): else: try: registration = TestCenterRegistration.objects.get(client_authorization_id=client_authorization_id) - Command.datadog_error("Found authorization record for user {}".format(registration.testcenter_user.user.username), eacfile) + Command.datadog_error("Found authorization record for user {}".format(registration.testcenter_user.user.username), eacfile.name) # now update the record: registration.upload_status = row['Status'] registration.upload_error_message = row['Message'] diff --git a/common/djangoapps/student/management/commands/pearson_make_tc_registration.py b/common/djangoapps/student/management/commands/pearson_make_tc_registration.py index 2fcfa9ae48..b59241240d 100644 --- a/common/djangoapps/student/management/commands/pearson_make_tc_registration.py +++ b/common/djangoapps/student/management/commands/pearson_make_tc_registration.py @@ -179,7 +179,8 @@ class Command(BaseCommand): if (len(form.errors) > 0): print "Field Form errors encountered:" for fielderror in form.errors: - print "Field Form Error: %s" % fielderror + for msg in form.errors[fielderror]: + print "Field Form Error: {} -- {}".format(fielderror, msg) if (len(form.non_field_errors()) > 0): print "Non-field Form errors encountered:" for nonfielderror in form.non_field_errors: diff --git a/common/djangoapps/student/models.py b/common/djangoapps/student/models.py index 44b947c045..838680c844 100644 --- a/common/djangoapps/student/models.py +++ b/common/djangoapps/student/models.py @@ -255,9 +255,9 @@ class TestCenterUserForm(ModelForm): def clean_country(self): code = self.cleaned_data['country'] - if code and len(code) != 3: + if code and (len(code) != 3 or not code.isalpha()): raise forms.ValidationError(u'Must be three characters (ISO 3166-1): e.g. USA, CAN, MNG') - return code + return code.upper() def clean(self): def _can_encode_as_latin(fieldvalue): @@ -387,6 +387,12 @@ class TestCenterRegistration(models.Model): return 'Update' elif self.uploaded_at is None: return 'Add' + elif self.registration_is_rejected: + # Assume that if the registration was rejected before, + # it is more likely this is the (first) correction + # than a second correction in flight before the first was + # processed. + return 'Add' else: # TODO: decide what to send when we have uploaded an initial version, # but have not received confirmation back from that upload. If the @@ -400,7 +406,8 @@ class TestCenterRegistration(models.Model): @property def exam_authorization_count(self): - # TODO: figure out if this should really go in the database (with a default value). + # Someday this could go in the database (with a default value). But at present, + # we do not expect anyone to be authorized to take an exam more than once. return 1 @property @@ -499,6 +506,33 @@ class TestCenterRegistration(models.Model): def registration_signup_url(self): return settings.PEARSONVUE_SIGNINPAGE_URL + def demographics_status(self): + if self.demographics_is_accepted: + return "Accepted" + elif self.demographics_is_rejected: + return "Rejected" + else: + return "Pending" + + def accommodation_status(self): + if self.accommodation_is_skipped: + return "Skipped" + elif self.accommodation_is_accepted: + return "Accepted" + elif self.accommodation_is_rejected: + return "Rejected" + else: + return "Pending" + + def registration_status(self): + if self.registration_is_accepted: + return "Accepted" + elif self.registration_is_rejected: + return "Rejected" + else: + return "Pending" + + class TestCenterRegistrationForm(ModelForm): class Meta: model = TestCenterRegistration @@ -518,7 +552,15 @@ class TestCenterRegistrationForm(ModelForm): 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)) - # TODO: add validation code for values added to accommodation_code field. + def clean_accommodation_code(self): + code = self.cleaned_data['accommodation_code'] + if code: + code = code.upper() + codes = code.split('*') + for codeval in codes: + if codeval not in ACCOMMODATION_CODE_DICT: + raise forms.ValidationError(u'Invalid accommodation code specified: "{}"'.format(codeval)) + return code