Commit 22ca961c by Braden MacDonald

Improved editors for MCQ/MRQ/Rating/Answer

parent 49472613
......@@ -48,13 +48,6 @@ class AnswerMixin(object):
"""
Mixin to give an XBlock the ability to read/write data to the Answers DB table.
"""
name = String(
display_name="Answer ID",
help="The ID of the long answer. Should be unique unless you want the answer to be used in multiple places.",
default="",
scope=Scope.content,
)
def _get_course_id(self):
""" Get a course ID if available """
return getattr(self.runtime, 'course_id', 'all')
......@@ -208,6 +201,11 @@ class AnswerRecapBlock(AnswerMixin, StudioEditableXBlockMixin, XBlock):
"""
A block that displays an answer previously entered by the student (read-only).
"""
name = String(
display_name="Answer ID",
help="The ID of the answer to display.",
scope=Scope.content,
)
display_name = String(
display_name="Title",
help="Title of this answer recap section",
......
......@@ -73,7 +73,7 @@ class ChoiceBlock(StudioEditableXBlockMixin, XBlock):
validation.add(ValidationMessage(ValidationMessage.ERROR, msg))
if not data.value.strip():
add_error(u"No value set yet.")
add_error(u"No value set. This choice will not work correctly.")
if not data.content.strip():
add_error(u"No choice text set yet.")
......
......@@ -48,9 +48,10 @@ class MCQBlock(QuestionnaireAbstractBlock):
correct_choices = List(
display_name="Correct Choice[s]",
help="Enter the value[s] that students may select for this question to be considered correct. ",
help="Specify the value[s] that students may select for this question to be considered correct.",
scope=Scope.content,
list_editor="comma-separated",
list_values_provider=QuestionnaireAbstractBlock.choice_values_provider,
list_style='set', # Underered, unique items. Affects the UI editor.
)
editable_fields = QuestionnaireAbstractBlock.editable_fields + ('correct_choices', )
......@@ -111,10 +112,11 @@ class RatingBlock(MCQBlock):
FIXED_VALUES = ["1", "2", "3", "4", "5"]
correct_choices = List(
display_name="Accepted Choice[s]",
help="Enter the rating value[s] that students may select for this question to be considered correct. ",
help="Specify the rating value[s] that students may select for this question to be considered correct.",
scope=Scope.content,
list_editor="comma-separated",
default=FIXED_VALUES,
list_values_provider=QuestionnaireAbstractBlock.choice_values_provider,
list_style='set', # Underered, unique items. Affects the UI editor.
)
editable_fields = MCQBlock.editable_fields + ('low', 'high')
......@@ -122,6 +124,13 @@ class RatingBlock(MCQBlock):
def all_choice_values(self):
return self.FIXED_VALUES + [c.value for c in self.custom_choices]
@property
def human_readable_choices(self):
display_names = ["1 - {}".format(self.low), "2", "3", "4", "5 - {}".format(self.high)]
return [
{"display_name": dn, "value": val} for val, dn in zip(self.FIXED_VALUES, display_names)
] + super(RatingBlock, self).human_readable_choices
def author_edit_view(self, context):
"""
The options for the 1-5 values of the Likert scale aren't child blocks but we want to
......
......@@ -45,22 +45,21 @@ class MRQBlock(QuestionnaireAbstractBlock):
student_choices = List(help="Last submissions by the student", default=[], scope=Scope.user_state)
required_choices = List(
display_name="Required Choices",
help=(
"Enter the value[s] that students must select for this MRQ to be considered correct. "
"Separate multiple required choices with a comma."
),
help=("Specify the value[s] that students must select for this MRQ to be considered correct. "),
scope=Scope.content,
list_editor="comma-separated",
list_values_provider=QuestionnaireAbstractBlock.choice_values_provider,
list_style='set', # Underered, unique items. Affects the UI editor.
default=[],
)
ignored_choices = List(
display_name="Ignored Choices",
help=(
"Enter the value[s] that are neither correct nor incorrect. "
"Specify the value[s] that are neither correct nor incorrect. "
"Any values not listed as required or ignored will be considered wrong."
),
scope=Scope.content,
list_editor="comma-separated",
list_values_provider=QuestionnaireAbstractBlock.choice_values_provider,
list_style='set', # Underered, unique items. Affects the UI editor.
default=[],
)
hide_results = Boolean(display_name="Hide results", scope=Scope.content, default=False)
......
......@@ -156,6 +156,18 @@ class QuestionnaireAbstractBlock(StudioEditableXBlockMixin, StudioContainerXBloc
def all_choice_values(self):
return [c.value for c in self.custom_choices]
@property
def human_readable_choices(self):
return [{"display_name": c.content, "value": c.value} for c in self.custom_choices]
@staticmethod
def choice_values_provider(question):
"""
Get a list a {"display_name": "Choice Description", "value": value}
objects for use with studio_view editor.
"""
return question.human_readable_choices
def get_tips(self):
"""
Returns the tips contained in this block
......
......@@ -23,6 +23,7 @@
# Imports ###########################################################
from django.utils.html import strip_tags
from lxml import etree
from xblock.core import XBlock
......@@ -32,18 +33,6 @@ from xblock.validation import ValidationMessage
from xblockutils.resources import ResourceLoader
from xblockutils.studio_editable import StudioEditableXBlockMixin
# Functions #########################################################
def commas_to_set(commas_str):
"""
Converts a comma-separated string to a set
"""
if not commas_str:
return set()
else:
return set(commas_str.split(','))
# Classes ###########################################################
......@@ -54,9 +43,11 @@ class TipBlock(StudioEditableXBlockMixin, XBlock):
content = String(help="Text of the tip to provide if needed", scope=Scope.content, default="")
values = List(
display_name="For Choices",
help="List of choice value[s] to display the tip for",
help="List of choices for which to display this tip",
scope=Scope.content,
default=[],
list_values_provider=lambda self: self.get_parent().human_readable_choices,
list_style='set', # Underered, unique items. Affects the UI editor.
)
width = String(help="Width of the tip popup", scope=Scope.content, default='')
height = String(help="Height of the tip popup", scope=Scope.content, default='')
......@@ -64,7 +55,14 @@ class TipBlock(StudioEditableXBlockMixin, XBlock):
@property
def display_name(self):
return u"Tip for {}".format(u", ".join([unicode(v) for v in self.values]))
values_list = []
for entry in self.get_parent().human_readable_choices:
if entry["value"] in self.values:
display_name = strip_tags(entry["display_name"]) # Studio studio_view can't handle html in display_name
if len(display_name) > 20:
display_name = display_name[:20] + u'…'
values_list.append(display_name)
return u"Tip for {}".format(u", ".join(values_list))
def fallback_view(self, view_name, context):
html = ResourceLoader(__name__).render_template("templates/html/tip.html", {
......
ddt
unicodecsv==0.9.4
-e git+https://github.com/open-craft/xblock-utils.git@c6a215884b59ca0449a2bda76fdd37b798a7aea9#egg=xblock-utils
-e git+https://github.com/open-craft/xblock-utils.git@1b8f21a9627f4d24041b6f7d8adc9f433e28ae07#egg=xblock-utils
-e .
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment