i18n for self-assessment (ORA-314)
make openendedchild and open_ended_module_v1 act enough like xblocks to get xblock 18n
This commit is contained in:
@@ -34,18 +34,31 @@ ACCEPT_FILE_UPLOAD = False
|
||||
# Contains all reasonable bool and case combinations of True
|
||||
TRUE_DICT = ["True", True, "TRUE", "true"]
|
||||
|
||||
_ = lambda text: text
|
||||
|
||||
HUMAN_TASK_TYPE = {
|
||||
'selfassessment': "Self",
|
||||
# Translators: "Self" is used to denote an openended response that is self-graded
|
||||
'selfassessment': _("Self"),
|
||||
'openended': "edX",
|
||||
'ml_grading.conf': "AI",
|
||||
'peer_grading.conf': "Peer",
|
||||
# Translators: "AI" is used to denote an openended response that is machine-graded
|
||||
'ml_grading.conf': _("AI"),
|
||||
# Translators: "Peer" is used to denote an openended response that is peer-graded
|
||||
'peer_grading.conf': _("Peer"),
|
||||
}
|
||||
|
||||
HUMAN_STATES = {
|
||||
'intitial': "Not started.",
|
||||
'assessing': "Being scored.",
|
||||
'intermediate_done': "Scoring finished.",
|
||||
'done': "Complete.",
|
||||
# Translators: "Not started" is used to communicate to a student that their response
|
||||
# has not yet been graded
|
||||
'intitial': _("Not started."),
|
||||
# Translators: "Being scored." is used to communicate to a student that their response
|
||||
# are in the process of being scored
|
||||
'assessing': _("Being scored."),
|
||||
# Translators: "Scoring finished" is used to communicate to a student that their response
|
||||
# have been scored, but the full scoring process is not yet complete
|
||||
'intermediate_done': _("Scoring finished."),
|
||||
# Translators: "Complete" is used to communicate to a student that their
|
||||
# openended response has been fully scored
|
||||
'done': _("Complete."),
|
||||
}
|
||||
|
||||
# Default value that controls whether or not to skip basic spelling checks in the controller
|
||||
@@ -86,6 +99,10 @@ class CombinedOpenEndedV1Module():
|
||||
# Where the templates live for this problem
|
||||
TEMPLATE_DIR = "combinedopenended"
|
||||
|
||||
# hack: included to make this class act enough like an xblock to get i18n
|
||||
_services_requested = {"i18n": "need"}
|
||||
_combined_services = _services_requested
|
||||
|
||||
def __init__(self, system, location, definition, descriptor,
|
||||
instance_state=None, shared_state=None, metadata=None, static_data=None, **kwargs):
|
||||
|
||||
@@ -541,6 +558,7 @@ class CombinedOpenEndedV1Module():
|
||||
"""
|
||||
task_html = self.get_html_base()
|
||||
# set context variables and render template
|
||||
ugettext = self.system.service(self, "i18n").ugettext
|
||||
|
||||
context = {
|
||||
'items': [{'content': task_html}],
|
||||
@@ -549,12 +567,12 @@ class CombinedOpenEndedV1Module():
|
||||
'state': self.state,
|
||||
'task_count': len(self.task_xml),
|
||||
'task_number': self.current_task_number + 1,
|
||||
'status': self.get_status(False),
|
||||
'status': ugettext(self.get_status(False)),
|
||||
'display_name': self.display_name,
|
||||
'accept_file_upload': self.accept_file_upload,
|
||||
'location': self.location,
|
||||
'legend_list': LEGEND_LIST,
|
||||
'human_state': HUMAN_STATES.get(self.state, "Not started."),
|
||||
'human_state': ugettext(HUMAN_STATES.get(self.state, "Not started.")),
|
||||
'is_staff': self.system.user_is_staff,
|
||||
}
|
||||
|
||||
@@ -567,7 +585,9 @@ class CombinedOpenEndedV1Module():
|
||||
Output: rendered html
|
||||
"""
|
||||
context = self.get_context()
|
||||
html = self.system.render_template('{0}/combined_open_ended.html'.format(self.TEMPLATE_DIR), context)
|
||||
html = self.system.render_template(
|
||||
'{0}/combined_open_ended.html'.format(self.TEMPLATE_DIR), context
|
||||
)
|
||||
return html
|
||||
|
||||
def get_html_nonsystem(self):
|
||||
@@ -578,7 +598,9 @@ class CombinedOpenEndedV1Module():
|
||||
Output: HTML rendered directly via Mako
|
||||
"""
|
||||
context = self.get_context()
|
||||
html = self.system.render_template('{0}/combined_open_ended.html'.format(self.TEMPLATE_DIR), context)
|
||||
html = self.system.render_template(
|
||||
'{0}/combined_open_ended.html'.format(self.TEMPLATE_DIR), context
|
||||
)
|
||||
return html
|
||||
|
||||
def get_html_base(self):
|
||||
@@ -815,6 +837,7 @@ class CombinedOpenEndedV1Module():
|
||||
Input: AJAX data dictionary
|
||||
Output: Dictionary to be rendered via ajax that contains the result html.
|
||||
"""
|
||||
ugettext = self.system.service(self, "i18n").ugettext
|
||||
all_responses = []
|
||||
success, can_see_rubric, error = self.check_if_student_has_done_needed_grading()
|
||||
if not can_see_rubric:
|
||||
@@ -841,12 +864,20 @@ class CombinedOpenEndedV1Module():
|
||||
rubric_scores = [[response['rubric_scores'][z]]]
|
||||
grader_types = [[response['grader_types'][z]]]
|
||||
feedback_items = [[response['feedback_items'][z]]]
|
||||
rubric_html = self.rubric_renderer.render_combined_rubric(stringify_children(self.static_data['rubric']),
|
||||
rubric_scores,
|
||||
grader_types, feedback_items)
|
||||
rubric_html = self.rubric_renderer.render_combined_rubric(
|
||||
stringify_children(self.static_data['rubric']),
|
||||
rubric_scores,
|
||||
grader_types,
|
||||
feedback_items
|
||||
)
|
||||
contexts.append({
|
||||
'result': rubric_html,
|
||||
'task_name': 'Scored rubric',
|
||||
# Translators: "Scored rubric" appears to a user as part of a longer
|
||||
# string that looks something like: "Scored rubric from grader 1".
|
||||
# "Scored" is an adjective that modifies the noun "rubric".
|
||||
# That longer string appears when a user is viewing a graded rubric
|
||||
# returned from one of the graders of their openended response problem.
|
||||
'task_name': ugettext('Scored rubric'),
|
||||
'feedback' : feedback
|
||||
})
|
||||
|
||||
@@ -925,6 +956,7 @@ class CombinedOpenEndedV1Module():
|
||||
Input: AJAX data dictionary
|
||||
Output: AJAX dictionary to tbe rendered
|
||||
"""
|
||||
ugettext = self.system.service(self, "i18n").ugettext
|
||||
if self.state != self.DONE:
|
||||
if not self.ready_to_reset:
|
||||
return self.out_of_sync_error(data)
|
||||
@@ -937,10 +969,13 @@ class CombinedOpenEndedV1Module():
|
||||
return {
|
||||
'success': False,
|
||||
# This is a student_facing_error
|
||||
'error': (
|
||||
'You have attempted this question {0} times. '
|
||||
'You are only allowed to attempt it {1} times.'
|
||||
).format(self.student_attempts, self.max_attempts)
|
||||
'error': ugettext(
|
||||
'You have attempted this question {number_of_student_attempts} times. '
|
||||
'You are only allowed to attempt it {max_number_of_attempts} times.'
|
||||
).format(
|
||||
number_of_student_attempts=self.student_attempts,
|
||||
max_number_of_attempts=self.max_attempts
|
||||
)
|
||||
}
|
||||
self.student_attempts +=1
|
||||
self.state = self.INITIAL
|
||||
@@ -980,26 +1015,32 @@ class CombinedOpenEndedV1Module():
|
||||
Input: None
|
||||
Output: The status html to be rendered
|
||||
"""
|
||||
status = []
|
||||
ugettext = self.system.service(self, "i18n").ugettext
|
||||
status_list = []
|
||||
current_task_human_name = ""
|
||||
for i in xrange(0, len(self.task_xml)):
|
||||
human_task_name = self.extract_human_name_from_task(self.task_xml[i])
|
||||
|
||||
human_task_name = ugettext(human_task_name)
|
||||
# Extract the name of the current task for screen readers.
|
||||
if self.current_task_number == i:
|
||||
current_task_human_name = human_task_name
|
||||
task_data = {'task_number': i + 1, 'human_task': human_task_name, 'current': self.current_task_number==i}
|
||||
status.append(task_data)
|
||||
task_data = {
|
||||
'task_number': i + 1,
|
||||
'human_task': human_task_name,
|
||||
'current': self.current_task_number == i
|
||||
}
|
||||
status_list.append(task_data)
|
||||
|
||||
context = {
|
||||
'status_list': status,
|
||||
'status_list': status_list,
|
||||
'grader_type_image_dict': GRADER_TYPE_IMAGE_DICT,
|
||||
'legend_list': LEGEND_LIST,
|
||||
'render_via_ajax': render_via_ajax,
|
||||
'current_task_human_name': current_task_human_name,
|
||||
}
|
||||
status_html = self.system.render_template("{0}/combined_open_ended_status.html".format(self.TEMPLATE_DIR),
|
||||
context)
|
||||
status_html = self.system.render_template(
|
||||
"{0}/combined_open_ended_status.html".format(self.TEMPLATE_DIR), context
|
||||
)
|
||||
|
||||
return status_html
|
||||
|
||||
@@ -1109,12 +1150,39 @@ class CombinedOpenEndedV1Module():
|
||||
"""
|
||||
return dict out-of-sync error message, and also log.
|
||||
"""
|
||||
ugettext = self.system.service(self, "i18n").ugettext
|
||||
#This is a dev_facing_error
|
||||
log.warning("Combined module state out sync. state: %r, data: %r. %s",
|
||||
self.state, data, msg)
|
||||
log.warning(
|
||||
"Combined module state out sync. state: %r, data: %r. %s",
|
||||
self.state,
|
||||
data,
|
||||
msg
|
||||
)
|
||||
#This is a student_facing_error
|
||||
return {'success': False,
|
||||
'error': 'The problem state got out-of-sync. Please try reloading the page.'}
|
||||
return {
|
||||
'success': False,
|
||||
'error': ugettext('The problem state got out-of-sync. Please try reloading the page.')
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def service_declaration(cls, service_name):
|
||||
"""
|
||||
This classmethod is copied from XBlock's service_declaration.
|
||||
It is included to make this class act enough like an XBlock
|
||||
to get i18n working on it.
|
||||
|
||||
This is currently only used for i18n, and will return "need"
|
||||
in that case.
|
||||
|
||||
Arguments:
|
||||
service_name (string): the name of the service requested.
|
||||
|
||||
Returns:
|
||||
One of "need", "want", or None.
|
||||
|
||||
"""
|
||||
declaration = cls._combined_services.get(service_name)
|
||||
return declaration
|
||||
|
||||
|
||||
class CombinedOpenEndedV1Descriptor():
|
||||
|
||||
@@ -11,12 +11,19 @@ GRADER_TYPE_IMAGE_DICT = {
|
||||
'BC': '/static/images/ml_grading_icon.png',
|
||||
}
|
||||
|
||||
_ = lambda text: text
|
||||
|
||||
HUMAN_GRADER_TYPE = {
|
||||
'SA': 'Self-Assessment',
|
||||
'PE': 'Peer-Assessment',
|
||||
'IN': 'Instructor-Assessment',
|
||||
'ML': 'AI-Assessment',
|
||||
'BC': 'AI-Assessment',
|
||||
# Translators: "Self-Assessment" refers to the self-assessed mode of openended evaluation
|
||||
'SA': _('Self-Assessment'),
|
||||
# Translators: "Peer-Assessment" refers to the peer-assessed mode of openended evaluation
|
||||
'PE': _('Peer-Assessment'),
|
||||
# Translators: "Instructor-Assessment" refers to the instructor-assessed mode of openended evaluation
|
||||
'IN': _('Instructor-Assessment'),
|
||||
# Translators: "AI-Assessment" refers to the machine-graded mode of openended evaluation
|
||||
'ML': _('AI-Assessment'),
|
||||
# Translators: "AI-Assessment" refers to the machine-graded mode of openended evaluation
|
||||
'BC': _('AI-Assessment'),
|
||||
}
|
||||
|
||||
DO_NOT_DISPLAY = ['BC', 'IN']
|
||||
@@ -63,13 +70,16 @@ class CombinedOpenEndedRubric(object):
|
||||
rubric_template = '{0}/open_ended_rubric.html'.format(self.TEMPLATE_DIR)
|
||||
if self.view_only:
|
||||
rubric_template = '{0}/open_ended_view_only_rubric.html'.format(self.TEMPLATE_DIR)
|
||||
html = self.system.render_template(rubric_template,
|
||||
{'categories': rubric_categories,
|
||||
'has_score': self.has_score,
|
||||
'view_only': self.view_only,
|
||||
'max_score': max_score,
|
||||
'combined_rubric': False
|
||||
})
|
||||
html = self.system.render_template(
|
||||
rubric_template,
|
||||
{
|
||||
'categories': rubric_categories,
|
||||
'has_score': self.has_score,
|
||||
'view_only': self.view_only,
|
||||
'max_score': max_score,
|
||||
'combined_rubric': False,
|
||||
}
|
||||
)
|
||||
success = True
|
||||
except:
|
||||
#This is a staff_facing_error
|
||||
@@ -153,7 +163,6 @@ class CombinedOpenEndedRubric(object):
|
||||
"[extract_category] Category {0} is missing a score. Contact the learning sciences group for assistance.".format(
|
||||
descriptionxml.text))
|
||||
|
||||
|
||||
# parse description
|
||||
if descriptionxml.tag != 'description':
|
||||
#This is a staff_facing_error
|
||||
@@ -245,17 +254,20 @@ class CombinedOpenEndedRubric(object):
|
||||
else:
|
||||
correct.append(.5)
|
||||
|
||||
html = self.system.render_template('{0}/open_ended_combined_rubric.html'.format(self.TEMPLATE_DIR),
|
||||
{'categories': rubric_categories,
|
||||
'max_scores': max_scores,
|
||||
'correct' : correct,
|
||||
'has_score': True,
|
||||
'view_only': True,
|
||||
'max_score': max_score,
|
||||
'combined_rubric': True,
|
||||
'grader_type_image_dict': GRADER_TYPE_IMAGE_DICT,
|
||||
'human_grader_types': HUMAN_GRADER_TYPE,
|
||||
})
|
||||
html = self.system.render_template(
|
||||
'{0}/open_ended_combined_rubric.html'.format(self.TEMPLATE_DIR),
|
||||
{
|
||||
'categories': rubric_categories,
|
||||
'max_scores': max_scores,
|
||||
'correct': correct,
|
||||
'has_score': True,
|
||||
'view_only': True,
|
||||
'max_score': max_score,
|
||||
'combined_rubric': True,
|
||||
'grader_type_image_dict': GRADER_TYPE_IMAGE_DICT,
|
||||
'human_grader_types': HUMAN_GRADER_TYPE,
|
||||
}
|
||||
)
|
||||
return html
|
||||
|
||||
@staticmethod
|
||||
|
||||
@@ -149,14 +149,22 @@ class OpenEndedModule(openendedchild.OpenEndedChild):
|
||||
event_info['problem_id'] = self.location_string
|
||||
event_info['student_id'] = system.anonymous_student_id
|
||||
event_info['survey_responses'] = data
|
||||
_ = self.system.service(self, "i18n").ugettext
|
||||
|
||||
survey_responses = event_info['survey_responses']
|
||||
for tag in ['feedback', 'submission_id', 'grader_id', 'score']:
|
||||
if tag not in survey_responses:
|
||||
# This is a student_facing_error
|
||||
return {'success': False,
|
||||
'msg': "Could not find needed tag {0} in the survey responses. Please try submitting again.".format(
|
||||
tag)}
|
||||
return {
|
||||
'success': False,
|
||||
# Translators: 'tag' is one of 'feedback', 'submission_id',
|
||||
# 'grader_id', or 'score'. They are categories that a student
|
||||
# responds to when filling out a post-assessment survey
|
||||
# of his or her grade from an openended problem.
|
||||
'msg': _("Could not find needed tag {tag_name} in the "
|
||||
"survey responses. Please try submitting "
|
||||
"again.").format(tag_name=tag)
|
||||
}
|
||||
try:
|
||||
submission_id = int(survey_responses['submission_id'])
|
||||
grader_id = int(survey_responses['grader_id'])
|
||||
@@ -171,11 +179,17 @@ class OpenEndedModule(openendedchild.OpenEndedChild):
|
||||
)
|
||||
log.exception(error_message)
|
||||
# This is a student_facing_error
|
||||
return {'success': False, 'msg': "There was an error saving your feedback. Please contact course staff."}
|
||||
return {
|
||||
'success': False,
|
||||
'msg': _(
|
||||
"There was an error saving your feedback. Please "
|
||||
"contact course staff."
|
||||
)
|
||||
}
|
||||
|
||||
xqueue = system.get('xqueue')
|
||||
if xqueue is None:
|
||||
return {'success': False, 'msg': "Couldn't submit feedback."}
|
||||
return {'success': False, 'msg': _("Couldn't submit feedback.")}
|
||||
qinterface = xqueue['interface']
|
||||
qtime = datetime.strftime(datetime.now(UTC), xqueue_interface.dateformat)
|
||||
anonymous_student_id = system.anonymous_student_id
|
||||
@@ -208,10 +222,10 @@ class OpenEndedModule(openendedchild.OpenEndedChild):
|
||||
|
||||
# Convert error to a success value
|
||||
success = True
|
||||
message = "Successfully saved your feedback."
|
||||
message = _("Successfully saved your feedback.")
|
||||
if error:
|
||||
success = False
|
||||
message = "Unable to save your feedback. Please try again later."
|
||||
message = _("Unable to save your feedback. Please try again later.")
|
||||
log.error("Unable to send feedback to grader. location: {0}, error_message: {1}".format(
|
||||
self.location_string, error_message
|
||||
))
|
||||
@@ -277,12 +291,14 @@ class OpenEndedModule(openendedchild.OpenEndedChild):
|
||||
'key': queuekey,
|
||||
'time': qtime,
|
||||
}
|
||||
|
||||
_ = self.system.service(self, "i18n").ugettext
|
||||
success = True
|
||||
message = "Successfully saved your submission."
|
||||
message = _("Successfully saved your submission.")
|
||||
if error:
|
||||
success = False
|
||||
message = 'Unable to submit your submission to grader. Please try again later.'
|
||||
# Translators: the `grader` refers to the grading service open response problems
|
||||
# are sent to, either to be machine-graded, peer-graded, or instructor-graded.
|
||||
message = _('Unable to submit your submission to the grader. Please try again later.')
|
||||
log.error("Unable to submit to grader. location: {0}, error_message: {1}".format(
|
||||
self.location_string, error_message
|
||||
))
|
||||
@@ -297,9 +313,12 @@ class OpenEndedModule(openendedchild.OpenEndedChild):
|
||||
@param system: Modulesystem
|
||||
@return: Boolean True (not useful currently)
|
||||
"""
|
||||
_ = self.system.service(self, "i18n").ugettext
|
||||
new_score_msg = self._parse_score_msg(score_msg, system)
|
||||
if not new_score_msg['valid']:
|
||||
new_score_msg['feedback'] = 'Invalid grader reply. Please contact the course staff.'
|
||||
# Translators: the `grader` refers to the grading service open response problems
|
||||
# are sent to, either to be machine-graded, peer-graded, or instructor-graded.
|
||||
new_score_msg['feedback'] = _('Invalid grader reply. Please contact the course staff.')
|
||||
|
||||
# self.child_history is initialized as []. record_latest_score() and record_latest_post_assessment()
|
||||
# operate on self.child_history[-1]. Thus we have to make sure child_history is not [].
|
||||
@@ -387,7 +406,7 @@ class OpenEndedModule(openendedchild.OpenEndedChild):
|
||||
|
||||
def format_feedback(feedback_type, value):
|
||||
feedback_type, value = encode_values(feedback_type, value)
|
||||
feedback = """
|
||||
feedback = u"""
|
||||
<div class="{feedback_type}">
|
||||
{value}
|
||||
</div>
|
||||
@@ -405,10 +424,15 @@ class OpenEndedModule(openendedchild.OpenEndedChild):
|
||||
# that we can do proper escaping here (e.g. are the graders allowed to
|
||||
# include HTML?)
|
||||
|
||||
_ = self.system.service(self, "i18n").ugettext
|
||||
for tag in ['success', 'feedback', 'submission_id', 'grader_id']:
|
||||
if tag not in response_items:
|
||||
# This is a student_facing_error
|
||||
return format_feedback('errors', 'Error getting feedback from grader.')
|
||||
return format_feedback(
|
||||
# Translators: the `grader` refers to the grading service open response problems
|
||||
# are sent to, either to be machine-graded, peer-graded, or instructor-graded.
|
||||
'errors', _('Error getting feedback from grader.')
|
||||
)
|
||||
|
||||
feedback_items = response_items['feedback']
|
||||
try:
|
||||
@@ -417,12 +441,20 @@ class OpenEndedModule(openendedchild.OpenEndedChild):
|
||||
# This is a dev_facing_error
|
||||
log.exception("feedback_items from external open ended grader have invalid json {0}".format(feedback_items))
|
||||
# This is a student_facing_error
|
||||
return format_feedback('errors', 'Error getting feedback from grader.')
|
||||
return format_feedback(
|
||||
# Translators: the `grader` refers to the grading service open response problems
|
||||
# are sent to, either to be machine-graded, peer-graded, or instructor-graded.
|
||||
'errors', _('Error getting feedback from grader.')
|
||||
)
|
||||
|
||||
if response_items['success']:
|
||||
if len(feedback) == 0:
|
||||
# This is a student_facing_error
|
||||
return format_feedback('errors', 'No feedback available from grader.')
|
||||
return format_feedback(
|
||||
# Translators: the `grader` refers to the grading service open response problems
|
||||
# are sent to, either to be machine-graded, peer-graded, or instructor-graded.
|
||||
'errors', _('No feedback available from grader.')
|
||||
)
|
||||
|
||||
for tag in do_not_render:
|
||||
if tag in feedback:
|
||||
@@ -648,12 +680,14 @@ class OpenEndedModule(openendedchild.OpenEndedChild):
|
||||
'check_for_score': self.check_for_score,
|
||||
'store_answer': self.store_answer,
|
||||
}
|
||||
|
||||
_ = self.system.service(self, "i18n").ugettext
|
||||
if dispatch not in handlers:
|
||||
# This is a dev_facing_error
|
||||
log.error("Cannot find {0} in handlers in handle_ajax function for open_ended_module.py".format(dispatch))
|
||||
# This is a dev_facing_error
|
||||
return json.dumps({'error': 'Error handling action. Please try again.', 'success': False})
|
||||
return json.dumps(
|
||||
{'error': _('Error handling action. Please try again.'), 'success': False}
|
||||
)
|
||||
|
||||
before = self.get_progress()
|
||||
d = handlers[dispatch](data, system)
|
||||
@@ -733,6 +767,7 @@ class OpenEndedModule(openendedchild.OpenEndedChild):
|
||||
Input: Modulesystem object
|
||||
Output: Rendered HTML
|
||||
"""
|
||||
_ = self.system.service(self, "i18n").ugettext
|
||||
# set context variables and render template
|
||||
eta_string = None
|
||||
if self.child_state != self.INITIAL:
|
||||
@@ -740,7 +775,9 @@ class OpenEndedModule(openendedchild.OpenEndedChild):
|
||||
score = self.latest_score()
|
||||
correct = 'correct' if self.is_submission_correct(score) else 'incorrect'
|
||||
if self.child_state == self.ASSESSING:
|
||||
eta_string = "Your response has been submitted. Please check back later for your grade."
|
||||
# Translators: this string appears once an openended response
|
||||
# is submitted but before it has been graded
|
||||
eta_string = _("Your response has been submitted. Please check back later for your grade.")
|
||||
else:
|
||||
post_assessment = ""
|
||||
correct = ""
|
||||
|
||||
@@ -25,9 +25,6 @@ MAX_ATTEMPTS = 1
|
||||
# Overriden by max_score specified in xml.
|
||||
MAX_SCORE = 1
|
||||
|
||||
FILE_NOT_FOUND_IN_RESPONSE_MESSAGE = "We could not find a file in your submission. Please try choosing a file or pasting a link to your file into the answer box."
|
||||
ERROR_SAVING_FILE_MESSAGE = "We are having trouble saving your file. Please try another file or paste a link to your file into the answer box."
|
||||
|
||||
|
||||
def upload_to_s3(file_to_upload, keyname, s3_interface):
|
||||
'''
|
||||
@@ -91,12 +88,23 @@ class OpenEndedChild(object):
|
||||
DONE = 'done'
|
||||
|
||||
# This is used to tell students where they are at in the module
|
||||
_ = lambda text: text
|
||||
HUMAN_NAMES = {
|
||||
'initial': 'Not started',
|
||||
'assessing': 'In progress',
|
||||
'post_assessment': 'Done',
|
||||
'done': 'Done',
|
||||
}
|
||||
# Translators: "Not started" communicates to a student that their response
|
||||
# has not yet been graded
|
||||
'initial': _('Not started'),
|
||||
# Translators: "In progress" communicates to a student that their response
|
||||
# is currently in the grading process
|
||||
'assessing': _('In progress'),
|
||||
# Translators: "Done" communicates to a student that their response
|
||||
# has been fully graded
|
||||
'post_assessment': _('Done'),
|
||||
'done': _('Done'),
|
||||
}
|
||||
|
||||
# included to make this act enough like an xblock to get i18n
|
||||
_services_requested = {"i18n": "need"}
|
||||
_combined_services = _services_requested
|
||||
|
||||
def __init__(self, system, location, definition, descriptor, static_data,
|
||||
instance_state=None, shared_state=None, **kwargs):
|
||||
@@ -463,6 +471,8 @@ class OpenEndedChild(object):
|
||||
@return: Boolean success, and updated AJAX data dictionary.
|
||||
"""
|
||||
|
||||
_ = self.system.service(self, "i18n").ugettext
|
||||
|
||||
error_message = ""
|
||||
|
||||
if not self.accept_file_upload:
|
||||
@@ -481,7 +491,11 @@ class OpenEndedChild(object):
|
||||
# If success is False, we have not found a link, and no file was attached.
|
||||
# Show error to student.
|
||||
if success is False:
|
||||
error_message = FILE_NOT_FOUND_IN_RESPONSE_MESSAGE
|
||||
error_message = _(
|
||||
"We could not find a file in your submission. "
|
||||
"Please try choosing a file or pasting a URL to your "
|
||||
"file into the answer box."
|
||||
)
|
||||
|
||||
except Exception:
|
||||
# In this case, an image was submitted by the student, but the image could not be uploaded to S3. Likely
|
||||
@@ -490,7 +504,10 @@ class OpenEndedChild(object):
|
||||
"but the image was not able to be uploaded to S3. This could indicate a configuration "
|
||||
"issue with this deployment and the S3_INTERFACE setting.")
|
||||
success = False
|
||||
error_message = ERROR_SAVING_FILE_MESSAGE
|
||||
error_message = _(
|
||||
"We are having trouble saving your file. Please try another "
|
||||
"file or paste a URL to your file into the answer box."
|
||||
)
|
||||
|
||||
return success, error_message, data
|
||||
|
||||
@@ -534,3 +551,23 @@ class OpenEndedChild(object):
|
||||
eta_string = ""
|
||||
|
||||
return eta_string
|
||||
|
||||
@classmethod
|
||||
def service_declaration(cls, service_name):
|
||||
"""
|
||||
This classmethod is copied from XBlock's service_declaration.
|
||||
It is included to make this class act enough like an XBlock
|
||||
to get i18n working on it.
|
||||
|
||||
This is currently only used for i18n, and will return "need"
|
||||
in that case.
|
||||
|
||||
Arguments:
|
||||
service_name (string): the name of the service requested.
|
||||
|
||||
Returns:
|
||||
One of "need", "want", or None.
|
||||
|
||||
"""
|
||||
declaration = cls._combined_services.get(service_name)
|
||||
return declaration
|
||||
|
||||
@@ -125,8 +125,9 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild):
|
||||
rubric_html = rubric_dict['html']
|
||||
|
||||
# we'll render it
|
||||
context = {'rubric': rubric_html,
|
||||
'max_score': self._max_score,
|
||||
context = {
|
||||
'rubric': rubric_html,
|
||||
'max_score': self._max_score,
|
||||
}
|
||||
|
||||
if self.child_state == self.ASSESSING:
|
||||
@@ -233,7 +234,11 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild):
|
||||
# This is a dev_facing_error
|
||||
log.error("Non-integer score value passed to save_assessment, or no score list present.")
|
||||
# This is a student_facing_error
|
||||
return {'success': False, 'error': "Error saving your score. Please notify course staff."}
|
||||
_ = self.system.service(self, "i18n").ugettext
|
||||
return {
|
||||
'success': False,
|
||||
'error': _("Error saving your score. Please notify course staff.")
|
||||
}
|
||||
|
||||
# Record score as assessment and rubric scores as post assessment
|
||||
self.record_latest_score(score)
|
||||
@@ -266,9 +271,11 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild):
|
||||
self.record_latest_post_assessment(data['hint'])
|
||||
self.change_state(self.DONE)
|
||||
|
||||
return {'success': True,
|
||||
'message_html': '',
|
||||
'allow_reset': self._allow_reset()}
|
||||
return {
|
||||
'success': True,
|
||||
'message_html': '',
|
||||
'allow_reset': self._allow_reset(),
|
||||
}
|
||||
|
||||
def latest_post_assessment(self, system):
|
||||
latest_post_assessment = super(SelfAssessmentModule, self).latest_post_assessment(system)
|
||||
|
||||
Reference in New Issue
Block a user