From 1d6b1be888b22edddf06cb549677d9d492cb3688 Mon Sep 17 00:00:00 2001 From: Brian Wilson Date: Wed, 16 Jan 2013 14:30:30 -0500 Subject: [PATCH 01/13] fix missing indirection in log message --- common/djangoapps/student/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/djangoapps/student/models.py b/common/djangoapps/student/models.py index 8220e5507c..6506e8b638 100644 --- a/common/djangoapps/student/models.py +++ b/common/djangoapps/student/models.py @@ -272,7 +272,7 @@ class TestCenterUserForm(ModelForm): # create additional values here: new_user.user_updated_at = datetime.utcnow() new_user.save() - log.info("Updated demographic information for user's test center exam registration: username \"{}\" ".format(new_user.username)) + log.info("Updated demographic information for user's test center exam registration: username \"{}\" ".format(new_user.user.username)) # add validation: From b16fa043710b85984ae36cfba876f8e260bf1da3 Mon Sep 17 00:00:00 2001 From: Brian Talbot Date: Wed, 16 Jan 2013 15:08:23 -0500 Subject: [PATCH 02/13] pearson registration - resolved box-sizing display issue that caused form fields to not show text/be editable in Firefox/Mozilla --- lms/static/sass/multicourse/_testcenter-register.scss | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lms/static/sass/multicourse/_testcenter-register.scss b/lms/static/sass/multicourse/_testcenter-register.scss index 961fffd5d0..6d85fc167f 100644 --- a/lms/static/sass/multicourse/_testcenter-register.scss +++ b/lms/static/sass/multicourse/_testcenter-register.scss @@ -232,8 +232,9 @@ $red: rgb(178, 6, 16); } input, textarea { + height: 100%; width: 100%; - padding: $baseline ($baseline*.75); + padding: ($baseline/2); &.long { width: 100%; From 1de67be788603405de8dfe3f6c478739b6ac0c80 Mon Sep 17 00:00:00 2001 From: Brian Wilson Date: Wed, 16 Jan 2013 15:43:15 -0500 Subject: [PATCH 03/13] clear upload_status if user makes changes to TestCenterUser or TestCenterRegistration (so the change is pending, rather than rejected) --- common/djangoapps/student/models.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/common/djangoapps/student/models.py b/common/djangoapps/student/models.py index 6506e8b638..7b4a5fb9be 100644 --- a/common/djangoapps/student/models.py +++ b/common/djangoapps/student/models.py @@ -271,6 +271,7 @@ class TestCenterUserForm(ModelForm): new_user = self.save(commit=False) # create additional values here: new_user.user_updated_at = datetime.utcnow() + new_user.upload_status = '' new_user.save() log.info("Updated demographic information for user's test center exam registration: username \"{}\" ".format(new_user.user.username)) @@ -533,6 +534,7 @@ class TestCenterRegistrationForm(ModelForm): registration = self.save(commit=False) # create additional values here: registration.user_updated_at = datetime.utcnow() + registration.upload_status = '' 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)) From e68e612ee120394f7731e740437a880d0f63a7d8 Mon Sep 17 00:00:00 2001 From: Brian Wilson Date: Wed, 16 Jan 2013 16:40:28 -0500 Subject: [PATCH 04/13] Return 404 in begin_exam_registration when course or exam are not valid --- common/djangoapps/student/views.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/common/djangoapps/student/views.py b/common/djangoapps/student/views.py index 8696c2ba28..f00e45d1fd 100644 --- a/common/djangoapps/student/views.py +++ b/common/djangoapps/student/views.py @@ -632,15 +632,18 @@ def begin_exam_registration(request, course_id): user = request.user try: - course = (course_from_id(course_id)) + course = course_from_id(course_id) except ItemNotFoundError: - # TODO: do more than just log!! The rest will fail, so we should fail right now. - log.error("User {0} enrolled in non-existent course {1}" - .format(user.username, course_id)) + log.error("User {0} enrolled in non-existent course {1}".format(user.username, course_id)) + raise Http404 # get the exam to be registered for: # (For now, we just assume there is one at most.) + # if there is no exam now (because someone bookmarked this stupid page), + # then return a 404: exam_info = course.current_test_center_exam + if exam_info is None: + raise Http404 # determine if the user is registered for this course: registration = exam_registration_info(user, course) From a756776836cc9a5c76f003e991900c8e9d14c40f Mon Sep 17 00:00:00 2001 From: Brian Talbot Date: Wed, 16 Jan 2013 17:26:29 -0500 Subject: [PATCH 05/13] pearson registration - revised rejection message text on dashboard, synced up email edx link text, revised contact styles/markup to remove giant red button dashboard for rejections --- lms/static/sass/multicourse/_dashboard.scss | 8 ++++++++ lms/templates/dashboard.html | 11 ++--------- lms/templates/test_center_register.html | 2 +- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/lms/static/sass/multicourse/_dashboard.scss b/lms/static/sass/multicourse/_dashboard.scss index c22bc14105..4555a426d3 100644 --- a/lms/static/sass/multicourse/_dashboard.scss +++ b/lms/static/sass/multicourse/_dashboard.scss @@ -426,6 +426,14 @@ font-size: 1.2rem; font-weight: bold; } + + strong { + font-weight: 700; + + a { + font-weight: 700; + } + } } .actions { diff --git a/lms/templates/dashboard.html b/lms/templates/dashboard.html index 0182a8edf1..8ec58a6a28 100644 --- a/lms/templates/dashboard.html +++ b/lms/templates/dashboard.html @@ -243,22 +243,15 @@ % endif % if registration.is_rejected:
-

Your - registration for the Pearson exam - has been rejected. Please check the information you provided, and try to correct any demographic errors. Otherwise - contact edX for further help.

- Contact exam-help@edx.org +

Your registration for the Pearson exam has been rejected. Please see your registration status details. Otherwise contact edX at exam-help@edx.org for further help.

% endif % if not registration.is_accepted and not registration.is_rejected:
-

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.

+

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.

% endif % endif - % endif <% diff --git a/lms/templates/test_center_register.html b/lms/templates/test_center_register.html index 03883d907c..4e1cece8c9 100644 --- a/lms/templates/test_center_register.html +++ b/lms/templates/test_center_register.html @@ -128,7 +128,7 @@ % if registration.registration_is_rejected:

Your registration for the Pearson exam has been rejected

-

Please see your registration status details for more information.

+

Please see your registration status details for more information.

% endif From fea6feb04fa434ecb07905caa123ea14879ce1bc Mon Sep 17 00:00:00 2001 From: Brian Wilson Date: Wed, 16 Jan 2013 17:39:33 -0500 Subject: [PATCH 06/13] Strip whitespace off of registration demographics before validation --- common/djangoapps/student/views.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/common/djangoapps/student/views.py b/common/djangoapps/student/views.py index f00e45d1fd..1a9648835e 100644 --- a/common/djangoapps/student/views.py +++ b/common/djangoapps/student/views.py @@ -678,11 +678,18 @@ def create_exam_registration(request, post_override=None): 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.... + course = course_from_id(course_id) # assume it will be found.... + + # make sure that any demographic data values received from the page have been stripped. + # Whitespace is not an acceptable response for any of these values + demographic_data = {} + for fieldname in TestCenterUser.user_provided_fields(): + if fieldname in post_vars: + demographic_data[fieldname] = (post_vars[fieldname]).strip() try: testcenter_user = TestCenterUser.objects.get(user=user) - needs_updating = testcenter_user.needs_update(post_vars) + needs_updating = testcenter_user.needs_update(demographic_data) log.info("User {0} enrolled in course {1} {2}updating demographic info for exam registration".format(user.username, course_id, "" if needs_updating else "not ")) except TestCenterUser.DoesNotExist: # do additional initialization here: @@ -694,7 +701,7 @@ def create_exam_registration(request, post_override=None): if needs_updating: # first perform validation on the user information # using a Django Form. - form = TestCenterUserForm(instance=testcenter_user, data=post_vars) + form = TestCenterUserForm(instance=testcenter_user, data=demographic_data) if form.is_valid(): form.update_and_save() else: From 4a59125bf900146cfbd033cb72bda91597a02e2b Mon Sep 17 00:00:00 2001 From: Brian Wilson Date: Wed, 16 Jan 2013 17:51:31 -0500 Subject: [PATCH 07/13] add pearson import confirmation tool --- .../commands/pearson_import_conf_zip.py | 131 ++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 common/djangoapps/student/management/commands/pearson_import_conf_zip.py diff --git a/common/djangoapps/student/management/commands/pearson_import_conf_zip.py b/common/djangoapps/student/management/commands/pearson_import_conf_zip.py new file mode 100644 index 0000000000..7fbc1ab484 --- /dev/null +++ b/common/djangoapps/student/management/commands/pearson_import_conf_zip.py @@ -0,0 +1,131 @@ +import csv + +from zipfile import ZipFile +from time import strptime, strftime + +from collections import OrderedDict +from datetime import datetime +from os.path import isdir +from optparse import make_option + +from django.core.management.base import BaseCommand, CommandError + +from student.models import TestCenterUser, TestCenterRegistration + +class Command(BaseCommand): + + + args = '' + help = """ + Import Pearson confirmation files and update TestCenterUser and TestCenterRegistration tables + with status. + """ + def handle(self, *args, **kwargs): + if len(args) < 1: + print Command.help + return + + source_zip = args[0] + # TODO: check that it's a zip + + + # loop through all files in zip, and process them based on filename prefix: + with ZipFile(source_zip, 'r') as zipfile: + for fileinfo in zipfile.infolist(): + with zipfile.open(fileinfo) as zipentry: + if fileinfo.filename.startswith("eac-"): + self.process_eac(zipentry) + elif fileinfo.filename.startswith("vcdc-"): + self.process_vcdc(zipentry) + else: + raise CommandError("Unrecognized confirmation file type \"{}\" in confirmation zip file \"{}\"".format(fileinfo.filename, zipfile)) + + def process_eac(self, eacfile): + print "processing eac" + reader = csv.DictReader(eacfile, delimiter="\t") + for row in reader: + client_authorization_id = row['ClientAuthorizationID'] + if client_authorization_id is not None: + try: + registration = TestCenterRegistration.objects.get(client_authorization_id=client_authorization_id) + print "Found authorization record for user {}".format(registration.testcenter_user.user.username) + # now update the record: + registration.upload_status = row['Status'] + registration.upload_error_message = row['Message'] + try: + registration.processed_at = strftime('%Y-%m-%d %H:%M:%S', strptime(row['Date'], '%Y/%m/%d %H:%M:%S')) + except ValueError as ve: + print "Bad Date value found for {}: message {}".format(client_authorization_id, ve) + # store the Authorization Id if one is provided. (For debugging) + if row['AuthorizationID']: + try: + registration.authorization_id = int(row['AuthorizationID']) + except ValueError as ve: + print "Bad AuthorizationID value found for {}: message {}".format(client_authorization_id, ve) + + registration.confirmed_at = datetime.utcnow() + registration.save() + except TestCenterRegistration.DoesNotExist: + print " Failed to find record for client_auth_id {}".format(client_authorization_id) + + def process_vcdc(self, vcdcfile): + print "processing vcdc" + reader = csv.DictReader(vcdcfile, delimiter="\t") + for row in reader: + client_candidate_id = row['ClientCandidateID'] + if client_candidate_id is not None: + try: + tcuser = TestCenterUser.objects.get(client_candidate_id=client_candidate_id) + print "Found demographics record for user {}".format(tcuser.user.username) + # now update the record: + tcuser.upload_status = row['Status'] + tcuser.upload_error_message = row['Message'] + try: + tcuser.processed_at = strftime('%Y-%m-%d %H:%M:%S', strptime(row['Date'], '%Y/%m/%d %H:%M:%S')) + except ValueError as ve: + print "Bad Date value found for {}: message {}".format(client_candidate_id, ve) + # store the Authorization Id if one is provided. (For debugging) + if row['CandidateID']: + try: + tcuser.candidate_id = int(row['CandidateID']) + except ValueError as ve: + print "Bad CandidateID value found for {}: message {}".format(client_candidate_id, ve) + tcuser.confirmed_at = datetime.utcnow() + tcuser.save() + except TestCenterUser.DoesNotExist: + print " Failed to find record for client_candidate_id {}".format(client_candidate_id) + + +# def _try_parse_time(self, key): +# """ +# Parse an optional metadata key containing a time: if present, complain +# if it doesn't parse. +# Return None if not present or invalid. +# """ +# if key in self.exam_info: +# try: +# return parse_time(self.exam_info[key]) +# except ValueError as e: +# msg = "Exam {0} in course {1} loaded with a bad exam_info key '{2}': '{3}'".format(self.exam_name, self.course_id, self.exam_info[key], e) +# log.warning(msg) +# return None +# +# with open(destfile, "wb") as outfile: +# writer = csv.DictWriter(outfile, +# Command.CSV_TO_MODEL_FIELDS, +# delimiter="\t", +# quoting=csv.QUOTE_MINIMAL, +# extrasaction='ignore') +# writer.writeheader() +# for tcu in TestCenterUser.objects.order_by('id'): +# if dump_all or tcu.needs_uploading: +# record = dict((csv_field, ensure_encoding(getattr(tcu, model_field))) +# for csv_field, model_field +# in Command.CSV_TO_MODEL_FIELDS.items()) +# record["LastUpdate"] = record["LastUpdate"].strftime("%Y/%m/%d %H:%M:%S") +# writer.writerow(record) +# tcu.uploaded_at = uploaded_at +# tcu.save() + + + From 80b2f45ac58674ce9f92c7920f1bddebbbb1d7e1 Mon Sep 17 00:00:00 2001 From: Brian Wilson Date: Thu, 17 Jan 2013 10:14:55 -0500 Subject: [PATCH 08/13] move pearson_import to another branch --- .../commands/pearson_import_conf_zip.py | 131 ------------------ 1 file changed, 131 deletions(-) delete mode 100644 common/djangoapps/student/management/commands/pearson_import_conf_zip.py diff --git a/common/djangoapps/student/management/commands/pearson_import_conf_zip.py b/common/djangoapps/student/management/commands/pearson_import_conf_zip.py deleted file mode 100644 index 7fbc1ab484..0000000000 --- a/common/djangoapps/student/management/commands/pearson_import_conf_zip.py +++ /dev/null @@ -1,131 +0,0 @@ -import csv - -from zipfile import ZipFile -from time import strptime, strftime - -from collections import OrderedDict -from datetime import datetime -from os.path import isdir -from optparse import make_option - -from django.core.management.base import BaseCommand, CommandError - -from student.models import TestCenterUser, TestCenterRegistration - -class Command(BaseCommand): - - - args = '' - help = """ - Import Pearson confirmation files and update TestCenterUser and TestCenterRegistration tables - with status. - """ - def handle(self, *args, **kwargs): - if len(args) < 1: - print Command.help - return - - source_zip = args[0] - # TODO: check that it's a zip - - - # loop through all files in zip, and process them based on filename prefix: - with ZipFile(source_zip, 'r') as zipfile: - for fileinfo in zipfile.infolist(): - with zipfile.open(fileinfo) as zipentry: - if fileinfo.filename.startswith("eac-"): - self.process_eac(zipentry) - elif fileinfo.filename.startswith("vcdc-"): - self.process_vcdc(zipentry) - else: - raise CommandError("Unrecognized confirmation file type \"{}\" in confirmation zip file \"{}\"".format(fileinfo.filename, zipfile)) - - def process_eac(self, eacfile): - print "processing eac" - reader = csv.DictReader(eacfile, delimiter="\t") - for row in reader: - client_authorization_id = row['ClientAuthorizationID'] - if client_authorization_id is not None: - try: - registration = TestCenterRegistration.objects.get(client_authorization_id=client_authorization_id) - print "Found authorization record for user {}".format(registration.testcenter_user.user.username) - # now update the record: - registration.upload_status = row['Status'] - registration.upload_error_message = row['Message'] - try: - registration.processed_at = strftime('%Y-%m-%d %H:%M:%S', strptime(row['Date'], '%Y/%m/%d %H:%M:%S')) - except ValueError as ve: - print "Bad Date value found for {}: message {}".format(client_authorization_id, ve) - # store the Authorization Id if one is provided. (For debugging) - if row['AuthorizationID']: - try: - registration.authorization_id = int(row['AuthorizationID']) - except ValueError as ve: - print "Bad AuthorizationID value found for {}: message {}".format(client_authorization_id, ve) - - registration.confirmed_at = datetime.utcnow() - registration.save() - except TestCenterRegistration.DoesNotExist: - print " Failed to find record for client_auth_id {}".format(client_authorization_id) - - def process_vcdc(self, vcdcfile): - print "processing vcdc" - reader = csv.DictReader(vcdcfile, delimiter="\t") - for row in reader: - client_candidate_id = row['ClientCandidateID'] - if client_candidate_id is not None: - try: - tcuser = TestCenterUser.objects.get(client_candidate_id=client_candidate_id) - print "Found demographics record for user {}".format(tcuser.user.username) - # now update the record: - tcuser.upload_status = row['Status'] - tcuser.upload_error_message = row['Message'] - try: - tcuser.processed_at = strftime('%Y-%m-%d %H:%M:%S', strptime(row['Date'], '%Y/%m/%d %H:%M:%S')) - except ValueError as ve: - print "Bad Date value found for {}: message {}".format(client_candidate_id, ve) - # store the Authorization Id if one is provided. (For debugging) - if row['CandidateID']: - try: - tcuser.candidate_id = int(row['CandidateID']) - except ValueError as ve: - print "Bad CandidateID value found for {}: message {}".format(client_candidate_id, ve) - tcuser.confirmed_at = datetime.utcnow() - tcuser.save() - except TestCenterUser.DoesNotExist: - print " Failed to find record for client_candidate_id {}".format(client_candidate_id) - - -# def _try_parse_time(self, key): -# """ -# Parse an optional metadata key containing a time: if present, complain -# if it doesn't parse. -# Return None if not present or invalid. -# """ -# if key in self.exam_info: -# try: -# return parse_time(self.exam_info[key]) -# except ValueError as e: -# msg = "Exam {0} in course {1} loaded with a bad exam_info key '{2}': '{3}'".format(self.exam_name, self.course_id, self.exam_info[key], e) -# log.warning(msg) -# return None -# -# with open(destfile, "wb") as outfile: -# writer = csv.DictWriter(outfile, -# Command.CSV_TO_MODEL_FIELDS, -# delimiter="\t", -# quoting=csv.QUOTE_MINIMAL, -# extrasaction='ignore') -# writer.writeheader() -# for tcu in TestCenterUser.objects.order_by('id'): -# if dump_all or tcu.needs_uploading: -# record = dict((csv_field, ensure_encoding(getattr(tcu, model_field))) -# for csv_field, model_field -# in Command.CSV_TO_MODEL_FIELDS.items()) -# record["LastUpdate"] = record["LastUpdate"].strftime("%Y/%m/%d %H:%M:%S") -# writer.writerow(record) -# tcu.uploaded_at = uploaded_at -# tcu.save() - - - From 59b328f4b2a9769e08be8a2eae935f049782d6dc Mon Sep 17 00:00:00 2001 From: Brian Talbot Date: Thu, 17 Jan 2013 10:49:57 -0500 Subject: [PATCH 09/13] pearson registration - removed telephone/fax placeholder attributes since they can't represent both domestic and international formatting easily --- lms/templates/test_center_register.html | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lms/templates/test_center_register.html b/lms/templates/test_center_register.html index 4e1cece8c9..1b45497ad7 100644 --- a/lms/templates/test_center_register.html +++ b/lms/templates/test_center_register.html @@ -246,25 +246,25 @@
  • - +
    - +
    - +
  • - +
    - +
  • From fdd395e0b31f79206d7fbda25196d505c83b8347 Mon Sep 17 00:00:00 2001 From: Brian Talbot Date: Thu, 17 Jan 2013 10:50:58 -0500 Subject: [PATCH 10/13] pearson registration - synced up link text for emailing edx --- lms/templates/test_center_register.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lms/templates/test_center_register.html b/lms/templates/test_center_register.html index 1b45497ad7..f6c53c0e89 100644 --- a/lms/templates/test_center_register.html +++ b/lms/templates/test_center_register.html @@ -474,7 +474,7 @@

    Questions

    -

    If you have a specific question pertaining to your registration, you may contact exam-help@edx.org.

    +

    If you have a specific question pertaining to your registration, you may contact edX at exam-help@edx.org.

    From 2d55f871eebb83833f194139c9199484dac5153f Mon Sep 17 00:00:00 2001 From: Ashley Penney Date: Thu, 17 Jan 2013 15:51:26 -0500 Subject: [PATCH 11/13] While this is a pain for content developer local environment updates it breaks the LMS in EC2 which is vastly more important. We'll jsut have to address helping individual content people with installing MySQL if needed. This is a real dependency in production and I think those belong in here. --- requirements.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 08cfe57e2e..bc019ab54c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -51,8 +51,7 @@ pygraphviz==1.1 pil==1.1.7 nltk==2.0.4 dogstatsd-python==0.2.1 -# Taking out MySQL-python for now because it requires mysql to be installed, so breaks updates on content folks' envs. -# MySQL-python +MySQL-python==1.2.4c1 sphinx==1.1.3 Shapely==1.2.16 ipython==0.13.1 From eaa6701c6fd3914653b6989455ac8b162bd9e2fa Mon Sep 17 00:00:00 2001 From: Vik Paruchuri Date: Thu, 17 Jan 2013 17:13:31 -0500 Subject: [PATCH 12/13] Remove mitxmako dependencies, make rubric and oechild inherit fro object --- .../xmodule/combined_open_ended_module.py | 14 +++++------ .../xmodule/combined_open_ended_rubric.py | 9 +++---- .../lib/xmodule/xmodule/open_ended_module.py | 25 +++++++++---------- common/lib/xmodule/xmodule/openendedchild.py | 4 +-- .../xmodule/xmodule/self_assessment_module.py | 4 +-- 5 files changed, 26 insertions(+), 30 deletions(-) diff --git a/common/lib/xmodule/xmodule/combined_open_ended_module.py b/common/lib/xmodule/xmodule/combined_open_ended_module.py index a88acc6ffd..5c8a88d9f7 100644 --- a/common/lib/xmodule/xmodule/combined_open_ended_module.py +++ b/common/lib/xmodule/xmodule/combined_open_ended_module.py @@ -22,8 +22,6 @@ from xmodule.modulestore import Location import self_assessment_module import open_ended_module -from mitxmako.shortcuts import render_to_string - log = logging.getLogger("mitx.courseware") # Set the default number of max attempts. Should be 1 for production @@ -319,7 +317,7 @@ class CombinedOpenEndedModule(XModule): Output: HTML rendered directly via Mako """ context = self.get_context() - html = render_to_string('combined_open_ended.html', context) + html = self.system.render_template('combined_open_ended.html', context) return html def get_html_base(self): @@ -369,17 +367,17 @@ class CombinedOpenEndedModule(XModule): self.static_data, instance_state=task_state) last_response = task.latest_answer() last_score = task.latest_score() - last_post_assessment = task.latest_post_assessment() + last_post_assessment = task.latest_post_assessment(self.system) last_post_feedback = "" if task_type == "openended": - last_post_assessment = task.latest_post_assessment(short_feedback=False, join_feedback=False) + last_post_assessment = task.latest_post_assessment(self.system, short_feedback=False, join_feedback=False) if isinstance(last_post_assessment, list): eval_list = [] for i in xrange(0, len(last_post_assessment)): - eval_list.append(task.format_feedback_with_evaluation(last_post_assessment[i])) + eval_list.append(task.format_feedback_with_evaluation(self.system, last_post_assessment[i])) last_post_evaluation = "".join(eval_list) else: - last_post_evaluation = task.format_feedback_with_evaluation(last_post_assessment) + last_post_evaluation = task.format_feedback_with_evaluation(self.system, last_post_assessment) last_post_assessment = last_post_evaluation last_correctness = task.is_last_response_correct() max_score = task.max_score() @@ -442,7 +440,7 @@ class CombinedOpenEndedModule(XModule): self.update_task_states() response_dict = self.get_last_response(task_number) context = {'results': response_dict['post_assessment'], 'task_number': task_number + 1} - html = render_to_string('combined_open_ended_results.html', context) + html = self.system.render_template('combined_open_ended_results.html', context) return {'html': html, 'success': True} def handle_ajax(self, dispatch, get): diff --git a/common/lib/xmodule/xmodule/combined_open_ended_rubric.py b/common/lib/xmodule/xmodule/combined_open_ended_rubric.py index 0b2ca1ca2c..e4daf11f1d 100644 --- a/common/lib/xmodule/xmodule/combined_open_ended_rubric.py +++ b/common/lib/xmodule/xmodule/combined_open_ended_rubric.py @@ -1,16 +1,15 @@ -from mitxmako.shortcuts import render_to_string import logging from lxml import etree log=logging.getLogger(__name__) -class CombinedOpenEndedRubric: +class CombinedOpenEndedRubric(object): @staticmethod - def render_rubric(rubric_xml): + def render_rubric(rubric_xml, system): try: rubric_categories = CombinedOpenEndedRubric.extract_rubric_categories(rubric_xml) - html = render_to_string('open_ended_rubric.html', {'rubric_categories' : rubric_categories}) + html = system.render_template('open_ended_rubric.html', {'rubric_categories' : rubric_categories}) except: log.exception("Could not parse the rubric.") html = rubric_xml @@ -64,7 +63,7 @@ class CombinedOpenEndedRubric: if has_score: if scorexml.tag != 'score': - raise Exception("[extract_category]: expected score tag, got {0} instead".format(scorexml.tag)) + raise Exception("[extract_category]mitxmako: expected score tag, got {0} instead".format(scorexml.tag)) for option in optionsxml: if option.tag != "option": diff --git a/common/lib/xmodule/xmodule/open_ended_module.py b/common/lib/xmodule/xmodule/open_ended_module.py index 11f96c9848..0eaad34bad 100644 --- a/common/lib/xmodule/xmodule/open_ended_module.py +++ b/common/lib/xmodule/xmodule/open_ended_module.py @@ -30,7 +30,6 @@ from xmodule.modulestore import Location from capa.util import * import openendedchild -from mitxmako.shortcuts import render_to_string from numpy import median from datetime import datetime @@ -256,7 +255,7 @@ class OpenEndedModule(openendedchild.OpenEndedChild): @param system: Modulesystem @return: Boolean True (not useful currently) """ - new_score_msg = self._parse_score_msg(score_msg) + new_score_msg = self._parse_score_msg(score_msg, system) if not new_score_msg['valid']: score_msg['feedback'] = 'Invalid grader reply. Please contact the course staff.' @@ -370,7 +369,7 @@ class OpenEndedModule(openendedchild.OpenEndedChild): return u"\n".join([feedback_list_part1, feedback_list_part2]) - def _format_feedback(self, response_items): + def _format_feedback(self, response_items, system): """ Input: Dictionary called feedback. Must contain keys seen below. @@ -382,13 +381,13 @@ class OpenEndedModule(openendedchild.OpenEndedChild): rubric_feedback="" feedback = self._convert_longform_feedback_to_html(response_items) if response_items['rubric_scores_complete']==True: - rubric_feedback = CombinedOpenEndedRubric.render_rubric(response_items['rubric_xml']) + rubric_feedback = CombinedOpenEndedRubric.render_rubric(response_items['rubric_xml'], system) if not response_items['success']: return system.render_template("open_ended_error.html", {'errors': feedback}) - feedback_template = render_to_string("open_ended_feedback.html", { + feedback_template = system.render_template("open_ended_feedback.html", { 'grader_type': response_items['grader_type'], 'score': "{0} / {1}".format(response_items['score'], self.max_score()), 'feedback': feedback, @@ -398,7 +397,7 @@ class OpenEndedModule(openendedchild.OpenEndedChild): return feedback_template - def _parse_score_msg(self, score_msg, join_feedback=True): + def _parse_score_msg(self, score_msg, system, join_feedback=True): """ Grader reply is a JSON-dump of the following dict { 'correct': True/False, @@ -450,7 +449,7 @@ class OpenEndedModule(openendedchild.OpenEndedChild): 'rubric_scores_complete' : score_result['rubric_scores_complete'], 'rubric_xml' : score_result['rubric_xml'], } - feedback_items.append(self._format_feedback(new_score_result)) + feedback_items.append(self._format_feedback(new_score_result, system)) if join_feedback: feedback = "".join(feedback_items) else: @@ -458,7 +457,7 @@ class OpenEndedModule(openendedchild.OpenEndedChild): score = int(median(score_result['score'])) else: #This is for instructor and ML grading - feedback = self._format_feedback(score_result) + feedback = self._format_feedback(score_result, system) score = score_result['score'] self.submission_id = score_result['submission_id'] @@ -466,7 +465,7 @@ class OpenEndedModule(openendedchild.OpenEndedChild): return {'valid': True, 'score': score, 'feedback': feedback} - def latest_post_assessment(self, short_feedback=False, join_feedback=True): + def latest_post_assessment(self, system, short_feedback=False, join_feedback=True): """ Gets the latest feedback, parses, and returns @param short_feedback: If the long feedback is wanted or not @@ -475,7 +474,7 @@ class OpenEndedModule(openendedchild.OpenEndedChild): if not self.history: return "" - feedback_dict = self._parse_score_msg(self.history[-1].get('post_assessment', ""), join_feedback=join_feedback) + feedback_dict = self._parse_score_msg(self.history[-1].get('post_assessment', ""), system, join_feedback=join_feedback) if not short_feedback: return feedback_dict['feedback'] if feedback_dict['valid'] else '' if feedback_dict['valid']: @@ -483,14 +482,14 @@ class OpenEndedModule(openendedchild.OpenEndedChild): json.loads(self.history[-1].get('post_assessment', ""))) return short_feedback if feedback_dict['valid'] else '' - def format_feedback_with_evaluation(self, feedback): + def format_feedback_with_evaluation(self, system, feedback): """ Renders a given html feedback into an evaluation template @param feedback: HTML feedback @return: Rendered html """ context = {'msg': feedback, 'id': "1", 'rows': 50, 'cols': 50} - html = render_to_string('open_ended_evaluation.html', context) + html = system.render_template('open_ended_evaluation.html', context) return html def handle_ajax(self, dispatch, get, system): @@ -582,7 +581,7 @@ class OpenEndedModule(openendedchild.OpenEndedChild): if self.state != self.INITIAL: latest = self.latest_answer() previous_answer = latest if latest is not None else self.initial_display - post_assessment = self.latest_post_assessment() + post_assessment = self.latest_post_assessment(system) score = self.latest_score() correct = 'correct' if self.is_submission_correct(score) else 'incorrect' else: diff --git a/common/lib/xmodule/xmodule/openendedchild.py b/common/lib/xmodule/xmodule/openendedchild.py index 2ba9528237..88fed61c6d 100644 --- a/common/lib/xmodule/xmodule/openendedchild.py +++ b/common/lib/xmodule/xmodule/openendedchild.py @@ -35,7 +35,7 @@ MAX_ATTEMPTS = 1 # Overriden by max_score specified in xml. MAX_SCORE = 1 -class OpenEndedChild(): +class OpenEndedChild(object): """ States: @@ -123,7 +123,7 @@ class OpenEndedChild(): return None return self.history[-1].get('score') - def latest_post_assessment(self): + def latest_post_assessment(self, system): """None if not available""" if not self.history: return "" diff --git a/common/lib/xmodule/xmodule/self_assessment_module.py b/common/lib/xmodule/xmodule/self_assessment_module.py index 940b61c557..3d88cb95f6 100644 --- a/common/lib/xmodule/xmodule/self_assessment_module.py +++ b/common/lib/xmodule/xmodule/self_assessment_module.py @@ -122,7 +122,7 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild): if self.state == self.INITIAL: return '' - rubric_html = CombinedOpenEndedRubric.render_rubric(self.rubric) + rubric_html = CombinedOpenEndedRubric.render_rubric(self.rubric, system) # we'll render it context = {'rubric': rubric_html, @@ -147,7 +147,7 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild): if self.state == self.DONE: # display the previous hint - latest = self.latest_post_assessment() + latest = self.latest_post_assessment(system) hint = latest if latest is not None else '' else: hint = '' From 18dc59c93ac9f6e2f5d427f07239ac3ef46c82f5 Mon Sep 17 00:00:00 2001 From: Vik Paruchuri Date: Thu, 17 Jan 2013 17:18:10 -0500 Subject: [PATCH 13/13] Error message fix --- common/lib/xmodule/xmodule/combined_open_ended_rubric.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/lib/xmodule/xmodule/combined_open_ended_rubric.py b/common/lib/xmodule/xmodule/combined_open_ended_rubric.py index e4daf11f1d..c2e0297038 100644 --- a/common/lib/xmodule/xmodule/combined_open_ended_rubric.py +++ b/common/lib/xmodule/xmodule/combined_open_ended_rubric.py @@ -63,7 +63,7 @@ class CombinedOpenEndedRubric(object): if has_score: if scorexml.tag != 'score': - raise Exception("[extract_category]mitxmako: expected score tag, got {0} instead".format(scorexml.tag)) + raise Exception("[extract_category]: expected score tag, got {0} instead".format(scorexml.tag)) for option in optionsxml: if option.tag != "option":