diff --git a/common/djangoapps/student/models.py b/common/djangoapps/student/models.py
index 8220e5507c..7b4a5fb9be 100644
--- a/common/djangoapps/student/models.py
+++ b/common/djangoapps/student/models.py
@@ -271,8 +271,9 @@ 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.username))
+ log.info("Updated demographic information for user's test center exam registration: username \"{}\" ".format(new_user.user.username))
# add validation:
@@ -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))
diff --git a/common/djangoapps/student/views.py b/common/djangoapps/student/views.py
index 8696c2ba28..61b49e6022 100644
--- a/common/djangoapps/student/views.py
+++ b/common/djangoapps/student/views.py
@@ -27,7 +27,7 @@ from bs4 import BeautifulSoup
from django.core.cache import cache
from django_future.csrf import ensure_csrf_cookie, csrf_exempt
-from student.models import (Registration, UserProfile, TestCenterUser, TestCenterUserForm,
+from student.models import (Registration, UserProfile, TestCenterUser, TestCenterUserForm,
TestCenterRegistration, TestCenterRegistrationForm,
PendingNameChange, PendingEmailChange,
CourseEnrollment, unique_id_for_user,
@@ -42,7 +42,7 @@ from xmodule.modulestore.django import modulestore
#from datetime import date
from collections import namedtuple
-from courseware.courses import get_courses
+from courseware.courses import get_courses, sort_by_announcement
from courseware.access import has_access
from statsd import statsd
@@ -78,10 +78,7 @@ def index(request, extra_context={}, user=None):
domain = request.META.get('HTTP_HOST')
courses = get_courses(None, domain=domain)
-
- # Sort courses by how far are they from they start day
- key = lambda course: course.days_until_start
- courses = sorted(courses, key=key, reverse=True)
+ courses = sort_by_announcement(courses)
# Get the 3 most recent news
top_news = _get_news(top=3)
@@ -211,7 +208,7 @@ def _cert_info(user, course, cert_status):
def dashboard(request):
user = request.user
enrollments = CourseEnrollment.objects.filter(user=user)
-
+
# Build our courses list for the user, but ignore any courses that no longer
# exist (because the course IDs have changed). Still, we don't delete those
# enrollments, because it could have been a data push snafu.
@@ -473,7 +470,7 @@ def _do_create_account(post_vars):
except (ValueError, KeyError):
# If they give us garbage, just ignore it instead
# of asking them to put an integer.
- profile.year_of_birth = None
+ profile.year_of_birth = None
try:
profile.save()
except Exception:
@@ -613,7 +610,7 @@ def exam_registration_info(user, course):
exam_info = course.current_test_center_exam
if exam_info is None:
return None
-
+
exam_code = exam_info.exam_series_code
registrations = get_testcenter_registration(user, course.id, exam_code)
if registrations:
@@ -621,7 +618,7 @@ def exam_registration_info(user, course):
else:
registration = None
return registration
-
+
@login_required
@ensure_csrf_cookie
def begin_exam_registration(request, course_id):
@@ -632,19 +629,22 @@ 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)
-
+
# we want to populate the registration page with the relevant information,
# if it already exists. Create an empty object otherwise.
try:
@@ -652,7 +652,7 @@ def begin_exam_registration(request, course_id):
except TestCenterUser.DoesNotExist:
testcenteruser = TestCenterUser()
testcenteruser.user = user
-
+
context = {'course': course,
'user': user,
'testcenteruser': testcenteruser,
@@ -669,17 +669,24 @@ def create_exam_registration(request, post_override=None):
Called by form in test_center_register.html
'''
post_vars = post_override if post_override else request.POST
-
- # first determine if we need to create a new TestCenterUser, or if we are making any update
+
+ # 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']
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:
@@ -689,9 +696,9 @@ def create_exam_registration(request, post_override=None):
# perform validation:
if needs_updating:
- # first perform validation on the user information
+ # 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:
@@ -700,7 +707,7 @@ def create_exam_registration(request, post_override=None):
response_data['field_errors'] = form.errors
response_data['non_field_errors'] = form.non_field_errors()
return HttpResponse(json.dumps(response_data), mimetype="application/json")
-
+
# create and save the registration:
needs_saving = False
exam = course.current_test_center_exam
@@ -710,12 +717,12 @@ def create_exam_registration(request, post_override=None):
registration = registrations[0]
# NOTE: we do not bother to check here to see if the registration has changed,
# because at the moment there is no way for a user to change anything about their
- # registration. They only provide an optional accommodation request once, and
+ # registration. They only provide an optional accommodation request once, and
# cannot make changes to it thereafter.
# It is possible that the exam_info content has been changed, such as the
# scheduled exam dates, but those kinds of changes should not be handled through
- # this registration screen.
-
+ # this registration screen.
+
else:
accommodation_request = post_vars.get('accommodation_request','')
registration = TestCenterRegistration.create(testcenter_user, exam, accommodation_request)
@@ -723,7 +730,7 @@ def create_exam_registration(request, post_override=None):
log.info("User {0} enrolled in course {1} creating new exam registration".format(user.username, course_id))
if needs_saving:
- # do validation of registration. (Mainly whether an accommodation request is too long.)
+ # do validation of registration. (Mainly whether an accommodation request is too long.)
form = TestCenterRegistrationForm(instance=registration, data=post_vars)
if form.is_valid():
form.update_and_save()
@@ -733,14 +740,14 @@ def create_exam_registration(request, post_override=None):
response_data['field_errors'] = form.errors
response_data['non_field_errors'] = form.non_field_errors()
return HttpResponse(json.dumps(response_data), mimetype="application/json")
-
+
# only do the following if there is accommodation text to send,
# and a destination to which to send it.
# TODO: still need to create the accommodation email templates
# if 'accommodation_request' in post_vars and 'TESTCENTER_ACCOMMODATION_REQUEST_EMAIL' in settings:
# d = {'accommodation_request': post_vars['accommodation_request'] }
-#
+#
# # composes accommodation email
# subject = render_to_string('emails/accommodation_email_subject.txt', d)
# # Email subject *must not* contain newlines
diff --git a/common/lib/xmodule/xmodule/combined_open_ended_module.py b/common/lib/xmodule/xmodule/combined_open_ended_module.py
index a35573032a..73caeb2358 100644
--- a/common/lib/xmodule/xmodule/combined_open_ended_module.py
+++ b/common/lib/xmodule/xmodule/combined_open_ended_module.py
@@ -24,8 +24,6 @@ import open_ended_module
from combined_open_ended_rubric import CombinedOpenEndedRubric
from .stringify import stringify_children
-from mitxmako.shortcuts import render_to_string
-
log = logging.getLogger("mitx.courseware")
# Set the default number of max attempts. Should be 1 for production
@@ -142,7 +140,7 @@ class CombinedOpenEndedModule(XModule):
# completion (doesn't matter if you self-assessed correct/incorrect).
self._max_score = int(self.metadata.get('max_score', MAX_SCORE))
- rubric_renderer = CombinedOpenEndedRubric(True)
+ rubric_renderer = CombinedOpenEndedRubric(system, True)
success, rubric_feedback = rubric_renderer.render_rubric(stringify_children(definition['rubric']))
if not success:
error_message="Could not parse rubric : {0}".format(definition['rubric'])
@@ -327,7 +325,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):
@@ -377,17 +375,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()
@@ -450,7 +448,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):
@@ -603,4 +601,4 @@ class CombinedOpenEndedDescriptor(XmlDescriptor, EditingDescriptor):
for child in ['task']:
add_child(child)
- return elt
\ No newline at end of file
+ return elt
diff --git a/common/lib/xmodule/xmodule/combined_open_ended_rubric.py b/common/lib/xmodule/xmodule/combined_open_ended_rubric.py
index 37ce18e4f1..07844aec35 100644
--- a/common/lib/xmodule/xmodule/combined_open_ended_rubric.py
+++ b/common/lib/xmodule/xmodule/combined_open_ended_rubric.py
@@ -1,14 +1,14 @@
-from mitxmako.shortcuts import render_to_string
import logging
from lxml import etree
log=logging.getLogger(__name__)
-class CombinedOpenEndedRubric:
+class CombinedOpenEndedRubric(object):
- def __init__ (self, view_only = False):
+ def __init__ (self, system, view_only = False):
self.has_score = False
self.view_only = view_only
+ self.system
'''
render_rubric: takes in an xml string and outputs the corresponding
@@ -23,7 +23,7 @@ class CombinedOpenEndedRubric:
success = False
try:
rubric_categories = self.extract_categories(rubric_xml)
- html = render_to_string('open_ended_rubric.html',
+ html = self.system.render_template('open_ended_rubric.html',
{'categories' : rubric_categories,
'has_score': self.has_score,
'view_only': self.view_only})
diff --git a/common/lib/xmodule/xmodule/course_module.py b/common/lib/xmodule/xmodule/course_module.py
index 499247cc2d..bc171ca5b9 100644
--- a/common/lib/xmodule/xmodule/course_module.py
+++ b/common/lib/xmodule/xmodule/course_module.py
@@ -1,4 +1,5 @@
import logging
+from math import exp, erf
from lxml import etree
from path import path # NOTE (THK): Only used for detecting presence of syllabus
import requests
@@ -183,35 +184,66 @@ class CourseDescriptor(SequenceDescriptor):
@property
def is_new(self):
- # The course is "new" if either if the metadata flag is_new is
- # true or if the course has not started yet
+ """
+ Returns if the course has been flagged as new in the metadata. If
+ there is no flag, return a heuristic value considering the
+ announcement and the start dates.
+ """
flag = self.metadata.get('is_new', None)
if flag is None:
- return self.days_until_start > 1
+ # Use a heuristic if the course has not been flagged
+ announcement, start, now = self._sorting_dates()
+ if announcement and (now - announcement).days < 30:
+ # The course has been announced for less that month
+ return True
+ elif (now - start).days < 1:
+ # The course has not started yet
+ return True
+ else:
+ return False
elif isinstance(flag, basestring):
return flag.lower() in ['true', 'yes', 'y']
else:
return bool(flag)
@property
- def days_until_start(self):
- def convert_to_datetime(timestamp):
+ def sorting_score(self):
+ """
+ Returns a number that can be used to sort the courses according
+ the how "new"" they are. The "newness"" score is computed using a
+ heuristic that takes into account the announcement and
+ (advertized) start dates of the course if available.
+
+ The lower the number the "newer" the course.
+ """
+ # Make courses that have an announcement date shave a lower
+ # score than courses than don't, older courses should have a
+ # higher score.
+ announcement, start, now = self._sorting_dates()
+ scale = 300.0 # about a year
+ if announcement:
+ days = (now - announcement).days
+ score = -exp(-days/scale)
+ else:
+ days = (now - start).days
+ score = exp(days/scale)
+ return score
+
+ def _sorting_dates(self):
+ # utility function to get datetime objects for dates used to
+ # compute the is_new flag and the sorting_score
+ def to_datetime(timestamp):
return datetime.fromtimestamp(time.mktime(timestamp))
- start_date = convert_to_datetime(self.start)
+ def get_date(field):
+ timetuple = self._try_parse_time(field)
+ return to_datetime(timetuple) if timetuple else None
- # Try to use course advertised date if we can parse it
- advertised_start = self.metadata.get('advertised_start', None)
- if advertised_start:
- try:
- start_date = datetime.strptime(advertised_start,
- "%Y-%m-%dT%H:%M")
- except ValueError:
- pass # Invalid date, keep using 'start''
+ announcement = get_date('announcement')
+ start = get_date('advertised_start') or to_datetime(self.start)
+ now = to_datetime(time.gmtime())
- now = convert_to_datetime(time.gmtime())
- days_until_start = (start_date - now).days
- return days_until_start
+ return announcement, start, now
@lazyproperty
def grading_context(self):
@@ -387,9 +419,9 @@ class CourseDescriptor(SequenceDescriptor):
self.first_eligible_appointment_date = self._try_parse_time('First_Eligible_Appointment_Date')
if self.first_eligible_appointment_date is None:
raise ValueError("First appointment date must be specified")
- # TODO: If defaulting the last appointment date, it should be the
+ # TODO: If defaulting the last appointment date, it should be the
# *end* of the same day, not the same time. It's going to be used as the
- # end of the exam overall, so we don't want the exam to disappear too soon.
+ # end of the exam overall, so we don't want the exam to disappear too soon.
# It's also used optionally as the registration end date, so time matters there too.
self.last_eligible_appointment_date = self._try_parse_time('Last_Eligible_Appointment_Date') # or self.first_eligible_appointment_date
if self.last_eligible_appointment_date is None:
@@ -403,7 +435,7 @@ class CourseDescriptor(SequenceDescriptor):
raise ValueError("First appointment date must be before last appointment date")
if self.registration_end_date > self.last_eligible_appointment_date:
raise ValueError("Registration end date must be before last appointment date")
-
+
def _try_parse_time(self, key):
"""
@@ -434,7 +466,7 @@ class CourseDescriptor(SequenceDescriptor):
def is_registering(self):
now = time.gmtime()
return now >= self.registration_start_date and now <= self.registration_end_date
-
+
@property
def first_eligible_appointment_date_text(self):
return time.strftime("%b %d, %Y", self.first_eligible_appointment_date)
@@ -451,7 +483,7 @@ class CourseDescriptor(SequenceDescriptor):
def current_test_center_exam(self):
exams = [exam for exam in self.test_center_exams if exam.has_started_registration() and not exam.has_ended()]
if len(exams) > 1:
- # TODO: output some kind of warning. This should already be
+ # TODO: output some kind of warning. This should already be
# caught if we decide to do validation at load time.
return exams[0]
elif len(exams) == 1:
diff --git a/common/lib/xmodule/xmodule/open_ended_module.py b/common/lib/xmodule/xmodule/open_ended_module.py
index 80cf6c4988..799e0ca29e 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,14 +381,18 @@ class OpenEndedModule(openendedchild.OpenEndedChild):
rubric_feedback=""
feedback = self._convert_longform_feedback_to_html(response_items)
if response_items['rubric_scores_complete']==True:
+<<<<<<< HEAD
rubric_renderer = CombinedOpenEndedRubric(True)
success, rubric_feedback = rubric_renderer.render_rubric(response_items['rubric_xml'])
+=======
+ rubric_feedback = CombinedOpenEndedRubric.render_rubric(response_items['rubric_xml'], system)
+>>>>>>> master
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,
@@ -399,7 +402,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,
@@ -451,7 +454,7 @@ class OpenEndedModule(openendedchild.OpenEndedChild):
'rubric_scores_complete' : score_result['rubric_scores_complete'][i],
'rubric_xml' : score_result['rubric_xml'][i],
}
- 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:
@@ -459,7 +462,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']
@@ -467,7 +470,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
@@ -476,7 +479,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']:
@@ -484,14 +487,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):
@@ -583,7 +586,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 de842091c9..9caba5a6fe 100644
--- a/common/lib/xmodule/xmodule/self_assessment_module.py
+++ b/common/lib/xmodule/xmodule/self_assessment_module.py
@@ -122,8 +122,7 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild):
if self.state == self.INITIAL:
return ''
- rubric_renderer = CombinedOpenEndedRubric(True)
-
+ rubric_renderer = CombinedOpenEndedRubric(system, True)
success, rubric_html = rubric_renderer.render_rubric(self.rubric)
# we'll render it
@@ -149,7 +148,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 = ''
diff --git a/common/lib/xmodule/xmodule/tests/test_course_module.py b/common/lib/xmodule/xmodule/tests/test_course_module.py
index 63eaec1f61..712b095696 100644
--- a/common/lib/xmodule/xmodule/tests/test_course_module.py
+++ b/common/lib/xmodule/xmodule/tests/test_course_module.py
@@ -1,5 +1,5 @@
import unittest
-from time import strptime, gmtime
+from time import strptime
from fs.memoryfs import MemoryFS
from mock import Mock, patch
@@ -39,52 +39,81 @@ class DummySystem(ImportSystem):
class IsNewCourseTestCase(unittest.TestCase):
"""Make sure the property is_new works on courses"""
@staticmethod
- def get_dummy_course(start, is_new=None, load_error_modules=True):
+ def get_dummy_course(start, announcement=None, is_new=None):
"""Get a dummy course"""
- system = DummySystem(load_error_modules)
- is_new = '' if is_new is None else 'is_new="{0}"'.format(is_new).lower()
+ system = DummySystem(load_error_modules=True)
+
+ def to_attrb(n, v):
+ return '' if v is None else '{0}="{1}"'.format(n, v).lower()
+
+ is_new = to_attrb('is_new', is_new)
+ announcement = to_attrb('announcement', announcement)
start_xml = '''
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.