Commit 22ca961c by Braden MacDonald

Improved editors for MCQ/MRQ/Rating/Answer

parent 49472613
...@@ -48,13 +48,6 @@ class AnswerMixin(object): ...@@ -48,13 +48,6 @@ class AnswerMixin(object):
""" """
Mixin to give an XBlock the ability to read/write data to the Answers DB table. 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): def _get_course_id(self):
""" Get a course ID if available """ """ Get a course ID if available """
return getattr(self.runtime, 'course_id', 'all') return getattr(self.runtime, 'course_id', 'all')
...@@ -208,6 +201,11 @@ class AnswerRecapBlock(AnswerMixin, StudioEditableXBlockMixin, XBlock): ...@@ -208,6 +201,11 @@ class AnswerRecapBlock(AnswerMixin, StudioEditableXBlockMixin, XBlock):
""" """
A block that displays an answer previously entered by the student (read-only). 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 = String(
display_name="Title", display_name="Title",
help="Title of this answer recap section", help="Title of this answer recap section",
......
...@@ -73,7 +73,7 @@ class ChoiceBlock(StudioEditableXBlockMixin, XBlock): ...@@ -73,7 +73,7 @@ class ChoiceBlock(StudioEditableXBlockMixin, XBlock):
validation.add(ValidationMessage(ValidationMessage.ERROR, msg)) validation.add(ValidationMessage(ValidationMessage.ERROR, msg))
if not data.value.strip(): 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(): if not data.content.strip():
add_error(u"No choice text set yet.") add_error(u"No choice text set yet.")
......
...@@ -48,9 +48,10 @@ class MCQBlock(QuestionnaireAbstractBlock): ...@@ -48,9 +48,10 @@ class MCQBlock(QuestionnaireAbstractBlock):
correct_choices = List( correct_choices = List(
display_name="Correct Choice[s]", 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, 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', ) editable_fields = QuestionnaireAbstractBlock.editable_fields + ('correct_choices', )
...@@ -111,10 +112,11 @@ class RatingBlock(MCQBlock): ...@@ -111,10 +112,11 @@ class RatingBlock(MCQBlock):
FIXED_VALUES = ["1", "2", "3", "4", "5"] FIXED_VALUES = ["1", "2", "3", "4", "5"]
correct_choices = List( correct_choices = List(
display_name="Accepted Choice[s]", 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, scope=Scope.content,
list_editor="comma-separated",
default=FIXED_VALUES, 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') editable_fields = MCQBlock.editable_fields + ('low', 'high')
...@@ -122,6 +124,13 @@ class RatingBlock(MCQBlock): ...@@ -122,6 +124,13 @@ class RatingBlock(MCQBlock):
def all_choice_values(self): def all_choice_values(self):
return self.FIXED_VALUES + [c.value for c in self.custom_choices] 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): 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 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): ...@@ -45,22 +45,21 @@ class MRQBlock(QuestionnaireAbstractBlock):
student_choices = List(help="Last submissions by the student", default=[], scope=Scope.user_state) student_choices = List(help="Last submissions by the student", default=[], scope=Scope.user_state)
required_choices = List( required_choices = List(
display_name="Required Choices", display_name="Required Choices",
help=( help=("Specify the value[s] that students must select for this MRQ to be considered correct. "),
"Enter the value[s] that students must select for this MRQ to be considered correct. "
"Separate multiple required choices with a comma."
),
scope=Scope.content, 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=[], default=[],
) )
ignored_choices = List( ignored_choices = List(
display_name="Ignored Choices", display_name="Ignored Choices",
help=( 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." "Any values not listed as required or ignored will be considered wrong."
), ),
scope=Scope.content, 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=[], default=[],
) )
hide_results = Boolean(display_name="Hide results", scope=Scope.content, default=False) hide_results = Boolean(display_name="Hide results", scope=Scope.content, default=False)
......
...@@ -156,6 +156,18 @@ class QuestionnaireAbstractBlock(StudioEditableXBlockMixin, StudioContainerXBloc ...@@ -156,6 +156,18 @@ class QuestionnaireAbstractBlock(StudioEditableXBlockMixin, StudioContainerXBloc
def all_choice_values(self): def all_choice_values(self):
return [c.value for c in self.custom_choices] 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): def get_tips(self):
""" """
Returns the tips contained in this block Returns the tips contained in this block
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
# Imports ########################################################### # Imports ###########################################################
from django.utils.html import strip_tags
from lxml import etree from lxml import etree
from xblock.core import XBlock from xblock.core import XBlock
...@@ -32,18 +33,6 @@ from xblock.validation import ValidationMessage ...@@ -32,18 +33,6 @@ from xblock.validation import ValidationMessage
from xblockutils.resources import ResourceLoader from xblockutils.resources import ResourceLoader
from xblockutils.studio_editable import StudioEditableXBlockMixin 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 ########################################################### # Classes ###########################################################
...@@ -54,9 +43,11 @@ class TipBlock(StudioEditableXBlockMixin, XBlock): ...@@ -54,9 +43,11 @@ class TipBlock(StudioEditableXBlockMixin, XBlock):
content = String(help="Text of the tip to provide if needed", scope=Scope.content, default="") content = String(help="Text of the tip to provide if needed", scope=Scope.content, default="")
values = List( values = List(
display_name="For Choices", 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, scope=Scope.content,
default=[], 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='') width = String(help="Width of the tip popup", scope=Scope.content, default='')
height = String(help="Height 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): ...@@ -64,7 +55,14 @@ class TipBlock(StudioEditableXBlockMixin, XBlock):
@property @property
def display_name(self): 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): def fallback_view(self, view_name, context):
html = ResourceLoader(__name__).render_template("templates/html/tip.html", { html = ResourceLoader(__name__).render_template("templates/html/tip.html", {
......
ddt ddt
unicodecsv==0.9.4 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 . -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