Commit 3736fa36 by Braden MacDonald

Made Rating a separate block, refactor MCQ/MRQ/Rating and make them editable

parent 47130c91
from .answer import AnswerBlock from .answer import AnswerBlock
from .choice import ChoiceBlock from .choice import ChoiceBlock
from .mcq import MCQBlock from .mcq import MCQBlock, RatingBlock
from .mrq import MRQBlock from .mrq import MRQBlock
from .message import MentoringMessageBlock from .message import MentoringMessageBlock
from .table import MentoringTableBlock, MentoringTableColumnBlock, MentoringTableColumnHeaderBlock from .table import MentoringTableBlock, MentoringTableColumnBlock, MentoringTableColumnHeaderBlock
......
...@@ -23,17 +23,73 @@ ...@@ -23,17 +23,73 @@
# Imports ########################################################### # Imports ###########################################################
from .common import BlockWithContent from lxml import etree
from xblock.core import XBlock
from xblock.fields import Scope, String from xblock.fields import Scope, String
from xblock.fragment import Fragment
from xblock.validation import ValidationMessage
from xblockutils.studio_editable import StudioEditableXBlockMixin
# Classes ########################################################### # Classes ###########################################################
class ChoiceBlock(BlockWithContent): class ChoiceBlock(StudioEditableXBlockMixin, XBlock):
""" """
Custom choice of an answer for a MCQ/MRQ Custom choice of an answer for a MCQ/MRQ
""" """
TEMPLATE = 'templates/html/choice.html' value = String(
display_name="Value",
help="Value of the choice when selected. Should be unique.",
scope=Scope.content,
default="",
)
content = String(
display_name="Choice Text",
help="Human-readable version of the choice value",
scope=Scope.content,
default="",
)
editable_fields = ('value', 'content')
@property
def display_name(self):
try:
status = self.get_parent().describe_choice_correctness(self.value)
except Exception:
status = u"Out of Context" # Parent block should implement describe_choice_correctness()
return u"Choice ({}) ({})".format(self.value, status)
def fallback_view(self, view_name, context):
return Fragment(u'<span class="choice-text">{}</span>'.format(self.content))
def validate_field_data(self, validation, data):
"""
Validate this block's field data.
"""
super(ChoiceBlock, self).validate_field_data(validation, data)
def add_error(msg):
validation.add(ValidationMessage(ValidationMessage.ERROR, msg))
if not data.value.strip():
add_error(u"No value set yet.")
if not data.content.strip():
add_error(u"No choice text set yet.")
@classmethod
def parse_xml(cls, node, runtime, keys, id_generator):
"""
Construct this XBlock from the given XML node.
"""
block = runtime.construct_xblock_from_class(cls, keys)
for field_name in cls.editable_fields:
if field_name in node.attrib:
setattr(block, field_name, node.attrib[field_name])
# HTML content:
block.content = unicode(node.text or u"")
for child in node:
block.content += etree.tostring(child, encoding='unicode')
value = String(help="Value of the choice when selected", scope=Scope.content, default="") return block
content = String(help="Human-readable version of the choice value", scope=Scope.content, default="")
# -*- coding: utf-8 -*-
#
# Copyright (c) 2014-2015 Harvard, edX & OpenCraft
#
# This software's license gives you freedom; you can copy, convey,
# propagate, redistribute and/or modify this program under the terms of
# the GNU Affero General Public License (AGPL) as published by the Free
# Software Foundation (FSF), either version 3 of the License, or (at your
# option) any later version of the AGPL published by the FSF.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
# General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program in a file in the toplevel directory called
# "AGPLv3". If not, see <http://www.gnu.org/licenses/>.
#
# Imports ###########################################################
from xblock.core import XBlock
from xblock.fields import Scope, String
from xblock.fragment import Fragment
from xblockutils.resources import ResourceLoader
# Classes ###########################################################
class BlockWithContent(XBlock):
"""
A block that can contain simple text content OR <html> blocks
with rich HTML content.
"""
TEMPLATE = None # Override in subclass
content = String(help="Content", scope=Scope.content, default="")
has_children = True
def fallback_view(self, view_name, context=None):
"""
Returns a fragment containing the HTML
"""
fragment = Fragment()
child_content = u""
for child_id in self.children:
child = self.runtime.get_block(child_id)
child_fragment = child.render('mentoring_view', {})
fragment.add_frag_resources(child_fragment)
child_content += child_fragment.content
fragment.add_content(ResourceLoader(__name__).render_template(self.TEMPLATE, {
'self': self,
'content': self.content,
'child_content': child_content,
}))
return fragment # TODO: fragment_text_rewriting
def get_html(self):
""" Render as HTML - not as a Fragment """
return self.fallback_view(None, None).content
...@@ -25,7 +25,8 @@ ...@@ -25,7 +25,8 @@
import logging import logging
from xblock.fields import Scope, String from xblock.fields import Scope, String, List
from xblock.fragment import Fragment
from xblockutils.resources import ResourceLoader from xblockutils.resources import ResourceLoader
from .questionnaire import QuestionnaireAbstractBlock from .questionnaire import QuestionnaireAbstractBlock
...@@ -34,6 +35,7 @@ from .questionnaire import QuestionnaireAbstractBlock ...@@ -34,6 +35,7 @@ from .questionnaire import QuestionnaireAbstractBlock
# Globals ########################################################### # Globals ###########################################################
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
loader = ResourceLoader(__name__)
# Classes ########################################################### # Classes ###########################################################
...@@ -42,22 +44,34 @@ class MCQBlock(QuestionnaireAbstractBlock): ...@@ -42,22 +44,34 @@ class MCQBlock(QuestionnaireAbstractBlock):
""" """
An XBlock used to ask multiple-choice questions An XBlock used to ask multiple-choice questions
""" """
type = String(help="Type of MCQ", scope=Scope.content, default="choices")
student_choice = String(help="Last input submitted by the student", default="", scope=Scope.user_state) student_choice = String(help="Last input submitted by the student", default="", scope=Scope.user_state)
low = String(help="Label for low ratings", scope=Scope.content, default="Less")
high = String(help="Label for high ratings", scope=Scope.content, default="More")
valid_types = ('rating', 'choices') correct_choices = List(
display_name="Correct Choice[s]",
help="Enter the value[s] that students may select for this question to be considered correct. ",
scope=Scope.content,
list_editor="comma-separated",
)
editable_fields = QuestionnaireAbstractBlock.editable_fields + ('correct_choices', )
def describe_choice_correctness(self, choice_value):
if choice_value in self.correct_choices:
if len(self.correct_choices) == 1:
return u"Correct"
return u"Acceptable"
else:
if len(self.correct_choices) == 1:
return u"Wrong"
return u"Not Acceptable"
def submit(self, submission): def submit(self, submission):
log.debug(u'Received MCQ submission: "%s"', submission) log.debug(u'Received MCQ submission: "%s"', submission)
correct = True correct = submission in self.correct_choices
tips_html = [] tips_html = []
for tip in self.get_tips(): for tip in self.get_tips():
correct = correct and self.is_tip_correct(tip, submission) if submission in tip.values:
if submission in tip.display_with_defaults: tips_html.append(tip.render('mentoring_view').content)
tips_html.append(tip.get_html())
formatted_tips = ResourceLoader(__name__).render_template('templates/html/tip_choice_group.html', { formatted_tips = ResourceLoader(__name__).render_template('templates/html/tip_choice_group.html', {
'self': self, 'self': self,
...@@ -76,11 +90,51 @@ class MCQBlock(QuestionnaireAbstractBlock): ...@@ -76,11 +90,51 @@ class MCQBlock(QuestionnaireAbstractBlock):
log.debug(u'MCQ submission result: %s', result) log.debug(u'MCQ submission result: %s', result)
return result return result
def is_tip_correct(self, tip, submission): def author_edit_view(self, context):
if not submission: """
return False The options for the 1-5 values of the Likert scale aren't child blocks but we want to
show them in the author edit view, for clarity.
"""
fragment = Fragment(u"<p>{}</p>".format(self.question))
self.render_children(context, fragment, can_reorder=True, can_add=False)
fragment.add_content(loader.render_template('templates/html/questionnaire_add_buttons.html', {}))
fragment.add_css_url(self.runtime.local_resource_url(self, 'public/css/questionnaire-edit.css'))
return fragment
if submission in tip.reject_with_defaults:
return False
return True class RatingBlock(MCQBlock):
"""
An XBlock used to rate something on a five-point scale, e.g. Likert Scale
"""
low = String(help="Label for low ratings", scope=Scope.content, default="Less")
high = String(help="Label for high ratings", scope=Scope.content, default="More")
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. ",
scope=Scope.content,
list_editor="comma-separated",
default=FIXED_VALUES,
)
editable_fields = MCQBlock.editable_fields + ('low', 'high')
@property
def all_choice_values(self):
return self.FIXED_VALUES + [c.value for c in self.custom_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
show them in the author edit view, for clarity.
"""
fragment = Fragment()
fragment.add_content(loader.render_template('templates/html/ratingblock_edit_preview.html', {
'question': self.question,
'low': self.low,
'high': self.high,
'accepted_statuses': [None] + [self.describe_choice_correctness(c) for c in "12345"],
}))
self.render_children(context, fragment, can_reorder=True, can_add=False)
fragment.add_content(loader.render_template('templates/html/questionnaire_add_buttons.html', {}))
fragment.add_css_url(self.runtime.local_resource_url(self, 'public/css/questionnaire-edit.css'))
return fragment
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
import logging import logging
from xblock.fields import List, Scope, Boolean from xblock.fields import List, Scope, Boolean
from xblock.validation import ValidationMessage
from .questionnaire import QuestionnaireAbstractBlock from .questionnaire import QuestionnaireAbstractBlock
from xblockutils.resources import ResourceLoader from xblockutils.resources import ResourceLoader
...@@ -42,7 +43,35 @@ class MRQBlock(QuestionnaireAbstractBlock): ...@@ -42,7 +43,35 @@ class MRQBlock(QuestionnaireAbstractBlock):
An XBlock used to ask multiple-response questions An XBlock used to ask multiple-response questions
""" """
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)
hide_results = Boolean(help="Hide results", scope=Scope.content, default=False) 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."
),
scope=Scope.content,
list_editor="comma-separated",
default=[],
)
ignored_choices = List(
display_name="Ignored Choices",
help=(
"Enter 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",
default=[],
)
hide_results = Boolean(display_name="Hide results", scope=Scope.content, default=False)
editable_fields = ('question', 'required_choices', 'ignored_choices', 'message', 'weight', 'hide_results', )
def describe_choice_correctness(self, choice_value):
if choice_value in self.required_choices:
return u"Required"
elif choice_value in self.ignored_choices:
return u"Ignored"
return u"Not Acceptable"
def submit(self, submissions): def submit(self, submissions):
log.debug(u'Received MRQ submissions: "%s"', submissions) log.debug(u'Received MRQ submissions: "%s"', submissions)
...@@ -54,13 +83,14 @@ class MRQBlock(QuestionnaireAbstractBlock): ...@@ -54,13 +83,14 @@ class MRQBlock(QuestionnaireAbstractBlock):
choice_completed = True choice_completed = True
choice_tips_html = [] choice_tips_html = []
choice_selected = choice.value in submissions choice_selected = choice.value in submissions
for tip in self.get_tips(): if choice.value in self.required_choices:
if choice.value in tip.display_with_defaults: if not choice_selected:
choice_tips_html.append(tip.get_html())
if ((not choice_selected and choice.value in tip.require_with_defaults) or
(choice_selected and choice.value in tip.reject_with_defaults)):
choice_completed = False choice_completed = False
elif choice_selected and choice.value not in self.ignored_choices:
choice_completed = False
for tip in self.get_tips():
if choice.value in tip.values:
choice_tips_html.append(tip.render('mentoring_view').content)
if choice_completed: if choice_completed:
score += 1 score += 1
...@@ -96,3 +126,27 @@ class MRQBlock(QuestionnaireAbstractBlock): ...@@ -96,3 +126,27 @@ class MRQBlock(QuestionnaireAbstractBlock):
log.debug(u'MRQ submissions result: %s', result) log.debug(u'MRQ submissions result: %s', result)
return result return result
def validate_field_data(self, validation, data):
"""
Validate this block's field data.
"""
super(MRQBlock, self).validate_field_data(validation, data)
def add_error(msg):
validation.add(ValidationMessage(ValidationMessage.ERROR, msg))
all_values = set(self.all_choice_values)
required = set(data.required_choices)
ignored = set(data.ignored_choices)
if len(required) < len(data.required_choices):
add_error(u"Duplicate required choices set")
if len(ignored) < len(data.ignored_choices):
add_error(u"Duplicate ignored choices set")
for val in required.intersection(ignored):
add_error(u"A choice is listed as both required and ignored: {}".format(val))
for val in (required - all_values):
add_error(u"A choice value listed as required does not exist: {}".format(val))
for val in (ignored - all_values):
add_error(u"A choice value listed as ignored does not exist: {}".format(val))
/* Custom appearance for our "Add" buttons */
.xblock .add-xblock-component .new-component .new-component-type .add-xblock-component-button {
width: 200px;
height: 30px;
line-height: 30px;
}
.wrapper-xblock.level-page .xblock-render {
padding: 10px;
}
...@@ -137,6 +137,10 @@ function MCQBlock(runtime, element) { ...@@ -137,6 +137,10 @@ function MCQBlock(runtime, element) {
}; };
} }
function RatingBlock(runtime, element) {
return MCQBlock(runtime, element);
}
function MRQBlock(runtime, element, mentoring) { function MRQBlock(runtime, element, mentoring) {
return { return {
mode: null, mode: null,
......
...@@ -25,18 +25,34 @@ ...@@ -25,18 +25,34 @@
from lxml import etree from lxml import etree
from xblock.core import XBlock from xblock.core import XBlock
from xblock.fields import Scope, String, Float from xblock.fields import Scope, String, Float, List
from xblock.fragment import Fragment from xblock.fragment import Fragment
from xblock.validation import ValidationMessage
from xblockutils.helpers import child_isinstance
from xblockutils.resources import ResourceLoader from xblockutils.resources import ResourceLoader
from xblockutils.studio_editable import StudioEditableXBlockMixin, StudioContainerXBlockMixin
from .choice import ChoiceBlock from .choice import ChoiceBlock
from .step import StepMixin from .step import StepMixin
from .tip import TipBlock from .tip import TipBlock
# Globals ###########################################################
loader = ResourceLoader(__name__)
# Classes ########################################################### # Classes ###########################################################
class QuestionnaireAbstractBlock(XBlock, StepMixin): class property_with_default(property):
"""
Decorator for creating a dynamic display_name property that looks like an XBlock field. This
is needed for Studio container page blocks as studio will try to read
BlockClass.display_name.default
"""
default = u"Question"
class QuestionnaireAbstractBlock(StudioEditableXBlockMixin, StudioContainerXBlockMixin, StepMixin, XBlock):
""" """
An abstract class used for MCQ/MRQ blocks An abstract class used for MCQ/MRQ blocks
...@@ -44,51 +60,68 @@ class QuestionnaireAbstractBlock(XBlock, StepMixin): ...@@ -44,51 +60,68 @@ class QuestionnaireAbstractBlock(XBlock, StepMixin):
values entered by the student, and supports multiple types of multiple-choice values entered by the student, and supports multiple types of multiple-choice
set, with preset choices and author-defined values. set, with preset choices and author-defined values.
""" """
type = String(help="Type of questionnaire", scope=Scope.content, default="choices") question = String(
question = String(help="Question to ask the student", scope=Scope.content, default="") display_name="Question",
message = String(help="General feedback provided when submiting", scope=Scope.content, default="") help="Question to ask the student",
weight = Float(help="Defines the maximum total grade of the light child block.", scope=Scope.content,
default=1, scope=Scope.content, enforce_type=True) default=""
)
valid_types = ('choices') message = String(
display_name="Message",
help="General feedback provided when submiting",
scope=Scope.content,
default=""
)
weight = Float(
display_name="Weight",
help="Defines the maximum total grade of this question.",
default=1,
scope=Scope.content,
enforce_type=True
)
editable_fields = ('question', 'message', 'weight')
has_children = True has_children = True
@classmethod @classmethod
def parse_xml(cls, node, runtime, keys, id_generator): def parse_xml(cls, node, runtime, keys, id_generator):
"""
Custom XML parser that can handle list type fields properly,
as well as the old way of defining 'question' and 'message' field values via tags.
"""
block = runtime.construct_xblock_from_class(cls, keys) block = runtime.construct_xblock_from_class(cls, keys)
# Load XBlock properties from the XML attributes: # Load XBlock properties from the XML attributes:
for name, value in node.items(): for name, value in node.items():
field = block.fields[name]
if isinstance(field, List) and not value.startswith('['):
# This list attribute is just a string of comma separated strings:
setattr(block, name, [unicode(val).strip() for val in value.split(',')])
elif isinstance(field, String):
setattr(block, name, value) setattr(block, name, value)
else:
setattr(block, name, field.from_json(value))
for xml_child in node: for xml_child in node:
if xml_child.tag == 'question': if xml_child.tag is not etree.Comment:
block.question = xml_child.text
elif xml_child.tag == 'message' and xml_child.get('type') == 'on-submit':
block.message = (xml_child.text or '').strip()
elif xml_child.tag is not etree.Comment:
block.runtime.add_node_as_child(block, xml_child, id_generator) block.runtime.add_node_as_child(block, xml_child, id_generator)
return block return block
@property_with_default
def display_name(self):
return u"Question {}".format(self.step_number) if not self.lonely_step else u"Question"
def student_view(self, context=None): def student_view(self, context=None):
name = getattr(self, "unmixed_class", self.__class__).__name__ name = getattr(self, "unmixed_class", self.__class__).__name__
if str(self.type) not in self.valid_types: template_path = 'templates/html/{}.html'.format(name.lower())
raise ValueError(u'Invalid value for {}.type: `{}`'.format(name, self.type))
template_path = 'templates/html/{}_{}.html'.format(name.lower(), self.type) context = context or {}
loader = ResourceLoader(__name__) context['self'] = self
context['custom_choices'] = self.custom_choices
html = loader.render_template(template_path, { fragment = Fragment(loader.render_template(template_path, context))
'self': self, fragment.add_css_url(self.runtime.local_resource_url(self, 'public/css/questionnaire.css'))
'custom_choices': self.custom_choices
})
fragment = Fragment(html)
fragment.add_css(loader.render_template('public/css/questionnaire.css', {
'self': self
}))
fragment.add_javascript_url(self.runtime.local_resource_url(self, 'public/js/questionnaire.js')) fragment.add_javascript_url(self.runtime.local_resource_url(self, 'public/js/questionnaire.js'))
fragment.initialize_js(name) fragment.initialize_js(name)
return fragment return fragment
...@@ -100,20 +133,22 @@ class QuestionnaireAbstractBlock(XBlock, StepMixin): ...@@ -100,20 +133,22 @@ class QuestionnaireAbstractBlock(XBlock, StepMixin):
def custom_choices(self): def custom_choices(self):
custom_choices = [] custom_choices = []
for child_id in self.children: for child_id in self.children:
child = self.runtime.get_block(child_id) if child_isinstance(self, child_id, ChoiceBlock):
if isinstance(child, ChoiceBlock): custom_choices.append(self.runtime.get_block(child_id))
custom_choices.append(child)
return custom_choices return custom_choices
@property
def all_choice_values(self):
return [c.value for c in self.custom_choices]
def get_tips(self): def get_tips(self):
""" """
Returns the tips contained in this block Returns the tips contained in this block
""" """
tips = [] tips = []
for child_id in self.children: for child_id in self.children:
child = self.runtime.get_block(child_id) if child_isinstance(self, child_id, TipBlock):
if isinstance(child, TipBlock): tips.append(self.runtime.get_block(child_id))
tips.append(child)
return tips return tips
def get_submission_display(self, submission): def get_submission_display(self, submission):
...@@ -124,3 +159,35 @@ class QuestionnaireAbstractBlock(XBlock, StepMixin): ...@@ -124,3 +159,35 @@ class QuestionnaireAbstractBlock(XBlock, StepMixin):
if choice.value == submission: if choice.value == submission:
return choice.content return choice.content
return submission return submission
def author_edit_view(self, context):
"""
Add some HTML to the author view that allows authors to add choices and tips.
"""
fragment = super(QuestionnaireAbstractBlock, self).author_edit_view(context)
fragment.add_content(loader.render_template('templates/html/questionnaire_add_buttons.html', {}))
fragment.add_css_url(self.runtime.local_resource_url(self, 'public/css/questionnaire-edit.css'))
return fragment
def validate(self):
"""
Validates the state of this XBlock.
"""
validation = super(QuestionnaireAbstractBlock, self).validate()
def add_error(msg):
validation.add(ValidationMessage(ValidationMessage.ERROR, msg))
# Validate the choice values:
all_choice_values = self.all_choice_values
all_choice_values_set = set(all_choice_values)
if len(all_choice_values) != len(all_choice_values_set):
add_error(u"Some choice values are not unique.")
# Validate the tips:
values_with_tips = set()
for tip in self.get_tips():
values = set(tip.values)
for val in (values & values_with_tips):
add_error(u"Multiple tips for value '{}'".format(val))
values_with_tips.update(values)
return validation
<span class="choice-text">
{{ self.content }}
{{ child_content|safe }}
</span>
<fieldset class="choices questionnaire"> <fieldset class="choices questionnaire">
<legend class="question"> <legend class="question">
<h3 class="question-title">QUESTION {% if not self.lonely_step %}{{ self.step_number }}{% endif %}</h3> {% if not hide_header %}<h3 class="question-title">{{ self.display_name }}</h3>{% endif %}
<p>{{ self.question }}</p> <p>{{ self.question }}</p>
</legend> </legend>
<div class="choices-list"> <div class="choices-list">
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
<div class="choice-result fa icon-2x"></div> <div class="choice-result fa icon-2x"></div>
<label class="choice-label"> <label class="choice-label">
<input class="choice-selector" type="radio" name="{{ self.name }}" value="{{ choice.value }}"{% if self.student_choice == choice.value %} checked{% endif %} /> <input class="choice-selector" type="radio" name="{{ self.name }}" value="{{ choice.value }}"{% if self.student_choice == choice.value %} checked{% endif %} />
{{ choice.get_html|safe }} <span class="choice-text">{{ choice.content|safe }}</span>
</label> </label>
<div class="choice-tips"></div> <div class="choice-tips"></div>
</div> </div>
......
<fieldset class="choices questionnaire" data-hide_results="{{self.hide_results}}"> <fieldset class="choices questionnaire" data-hide_results="{{self.hide_results}}">
<legend class="question"> <legend class="question">
<h3 class="question-title">QUESTION {% if not self.lonely_step %}{{ self.step_number }}{% endif %}</h3> {% if not hide_header %}<h3 class="question-title">{{ self.display_name }}</h3>{% endif %}
<p>{{ self.question }}</p> <p>{{ self.question }}</p>
</legend> </legend>
<div class="choices-list"> <div class="choices-list">
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
<input class="choice-selector" type="checkbox" name="{{ self.name }}" <input class="choice-selector" type="checkbox" name="{{ self.name }}"
value="{{ choice.value }}" value="{{ choice.value }}"
{% if choice.value in self.student_choices %} checked{% endif %} /> {% if choice.value in self.student_choices %} checked{% endif %} />
{{ choice.get_html|safe }} <span class="choice-text">{{ choice.content|safe }}</span>
</label> </label>
<div class="choice-tips"></div> <div class="choice-tips"></div>
</div> </div>
......
{% load i18n %}
<div class="add-xblock-component new-component-item adding">
<div class="new-component">
<ul class="new-component-type">
<li><a href="#" class="single-template add-xblock-component-button" data-category="mentoring-choice">Add Custom Choice</a></li>
<li><a href="#" class="single-template add-xblock-component-button" data-category="mentoring-tip">Add Tip</a></li>
</ul>
</div>
</div>
<fieldset class="rating questionnaire"> <fieldset class="rating questionnaire">
<legend class="question"> <legend class="question">
<h3 class="question-title">QUESTION {% if not self.lonely_step %}{{ self.step_number }}{% endif %}</h3> {% if not hide_header %}<h3 class="question-title">{{ self.display_name }}</h3>{% endif %}
<p>{{ self.question }}</p> <p>{{ self.question }}</p>
</legend> </legend>
<div class="choices-list"> <div class="choices-list">
...@@ -35,7 +35,7 @@ ...@@ -35,7 +35,7 @@
<div class="choice"> <div class="choice">
<div class="choice-result fa icon-2x"></div> <div class="choice-result fa icon-2x"></div>
<label><input type="radio" name="{{ self.name }}" value="{{ choice.value }}"{% if self.student_choice == '{{ choice.value }}' %} checked{% endif %} /> <label><input type="radio" name="{{ self.name }}" value="{{ choice.value }}"{% if self.student_choice == '{{ choice.value }}' %} checked{% endif %} />
{{ choice.get_html|safe }} <span class="choice-text">{{ choice.content|safe }}</span>
</label> </label>
<div class="choice-tips"></div> <div class="choice-tips"></div>
</div> </div>
......
<p>{{ question }}</p>
<h2>Built-in choices:</h2>
<ul>
<li>Choice (1): <strong>1 - {{ low }}</strong> ({{accepted_statuses.1}})</li>
<li>Choice (2): <strong>2</strong> ({{accepted_statuses.2}})</li>
<li>Choice (3): <strong>3</strong> ({{accepted_statuses.3}})</li>
<li>Choice (4): <strong>4</strong> ({{accepted_statuses.4}})</li>
<li>Choice (5): <strong>5 - {{ high }}</strong> ({{accepted_statuses.5}})</li>
</ul>
<h2>Additional custom choices and tips:</h2>
...@@ -3,8 +3,5 @@ ...@@ -3,8 +3,5 @@
{% if width %}data-width="{{width}}"{% endif %} {% if width %}data-width="{{width}}"{% endif %}
{% if height %}data-height="{{height}}"{% endif %} {% if height %}data-height="{{height}}"{% endif %}
> >
{% if self.content %} <p>{{ content|safe }}</p>
<p>{{ self.content }}</p>
{% endif %}
{{ child_content|safe }}
</div> </div>
...@@ -23,8 +23,14 @@ ...@@ -23,8 +23,14 @@
# Imports ########################################################### # Imports ###########################################################
from .common import BlockWithContent from lxml import etree
from xblock.fields import Scope, String
from xblock.core import XBlock
from xblock.fields import Scope, String, List
from xblock.fragment import Fragment
from xblock.validation import ValidationMessage
from xblockutils.resources import ResourceLoader
from xblockutils.studio_editable import StudioEditableXBlockMixin
# Functions ######################################################### # Functions #########################################################
...@@ -41,28 +47,70 @@ def commas_to_set(commas_str): ...@@ -41,28 +47,70 @@ def commas_to_set(commas_str):
# Classes ########################################################### # Classes ###########################################################
class TipBlock(BlockWithContent): class TipBlock(StudioEditableXBlockMixin, XBlock):
""" """
Each choice can define a tip depending on selection Each choice can define a tip depending on selection
""" """
TEMPLATE = 'templates/html/tip.html'
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="")
display = String(help="List of choices to display the tip for", scope=Scope.content, default=None) values = List(
reject = String(help="List of choices to reject", scope=Scope.content, default=None) display_name="For Choices",
require = String(help="List of choices to require", scope=Scope.content, default=None) help="List of choice value[s] to display the tip for",
scope=Scope.content,
default=[],
)
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='')
editable_fields = ('values', 'content', 'width', 'height')
@property @property
def display_with_defaults(self): def display_name(self):
display = commas_to_set(self.display) return u"Tip for {}".format(u", ".join([unicode(v) for v in self.values]))
return display | self.reject_with_defaults | self.require_with_defaults
@property def fallback_view(self, view_name, context):
def reject_with_defaults(self): html = ResourceLoader(__name__).render_template("templates/html/tip.html", {
return commas_to_set(self.reject) 'content': self.content,
'width': self.width,
'height': self.height,
})
return Fragment(html)
@property def clean_studio_edits(self, data):
def require_with_defaults(self): """
return commas_to_set(self.require) Clean up the edits during studio_view save
"""
if "values" in data:
data["values"] = list([unicode(v) for v in set(data["values"])])
def validate_field_data(self, validation, data):
"""
Validate this block's field data.
"""
super(TipBlock, self).validate_field_data(validation, data)
def add_error(msg):
validation.add(ValidationMessage(ValidationMessage.ERROR, msg))
try:
valid_values = set(self.get_parent().all_choice_values)
except Exception:
pass
else:
for val in set(data.values) - valid_values:
add_error(u"A choice value listed for this tip does not exist: {}".format(val))
@classmethod
def parse_xml(cls, node, runtime, keys, id_generator):
"""
Construct this XBlock from the given XML node.
"""
block = runtime.construct_xblock_from_class(cls, keys)
block.values = [unicode(val).strip() for val in node.get('values', '').split(',')]
block.width = node.get('width', '')
block.height = node.get('height', '')
block.content = unicode(node.text or u"")
for child in node:
block.content += etree.tostring(child, encoding='unicode')
return block
...@@ -9,35 +9,32 @@ ...@@ -9,35 +9,32 @@
<question>What is your goal?</question> <question>What is your goal?</question>
</answer> </answer>
<mcq name="mcq_1_1" type="choices"> <mcq name="mcq_1_1" question="Do you like this MCQ?" correct_choices="yes">
<question>Do you like this MCQ?</question>
<choice value="yes">Yes</choice> <choice value="yes">Yes</choice>
<choice value="maybenot">Maybe not</choice> <choice value="maybenot">Maybe not</choice>
<choice value="understand">I don't understand</choice> <choice value="understand">I don't understand</choice>
<tip display="yes">Great!</tip> <tip values="yes">Great!</tip>
<tip reject="maybenot">Ah, damn.</tip> <tip values="maybenot">Ah, damn.</tip>
<tip reject="understand"><html><div id="test-custom-html">Really?</div></html></tip> <tip values="understand"><div id="test-custom-html">Really?</div></tip>
</mcq> </mcq>
<mcq name="mcq_1_2" type="rating" low="Not good at all" high="Extremely good"> <rating name="mcq_1_2" low="Not good at all" high="Extremely good" question="How much do you rate this MCQ?" correct_choices="4,5">
<question>How much do you rate this MCQ?</question>
<choice value="notwant">I don't want to rate it</choice> <choice value="notwant">I don't want to rate it</choice>
<tip display="4,5">I love good grades.</tip> <tip values="4,5">I love good grades.</tip>
<tip reject="1,2,3">Will do better next time...</tip> <tip values="1,2,3">Will do better next time...</tip>
<tip reject="notwant">Your loss!</tip> <tip values="notwant">Your loss!</tip>
</mcq> </rating>
<mrq name="mrq_1_1" type="choices"> <mrq name="mrq_1_1" question="What do you like in this MRQ?" required_choices="gracefulness,elegance,beauty">
<question>What do you like in this MRQ?</question>
<choice value="elegance">Its elegance</choice> <choice value="elegance">Its elegance</choice>
<choice value="beauty">Its beauty</choice> <choice value="beauty">Its beauty</choice>
<choice value="gracefulness">Its gracefulness</choice> <choice value="gracefulness">Its gracefulness</choice>
<choice value="bugs">Its bugs</choice> <choice value="bugs">Its bugs</choice>
<tip require="gracefulness">This MRQ is indeed very graceful</tip> <tip values="gracefulness">This MRQ is indeed very graceful</tip>
<tip require="elegance,beauty">This is something everyone has to like about this MRQ</tip> <tip values="elegance,beauty">This is something everyone has to like about this MRQ</tip>
<tip reject="bugs">Nah, there isn't any!</tip> <tip values="bugs">Nah, there isn't any!</tip>
</mrq> </mrq>
</mentoring> </mentoring>
...@@ -7,38 +7,32 @@ ...@@ -7,38 +7,32 @@
<question>What is your goal?</question> <question>What is your goal?</question>
</answer> </answer>
<mcq name="mcq_1_1" type="choices"> <mcq name="mcq_1_1" question="Do you like this MCQ?" correct_choices="yes">
<question>Do you like this MCQ?</question>
<choice value="yes">Yes</choice> <choice value="yes">Yes</choice>
<choice value="maybenot">Maybe not</choice> <choice value="maybenot">Maybe not</choice>
<choice value="understand">I don't understand</choice> <choice value="understand">I don't understand</choice>
<tip display="yes">Great!</tip> <tip values="yes">Great!</tip>
<tip reject="maybenot">Ah, damn.</tip> <tip values="maybenot">Ah, damn.</tip>
<tip reject="understand"><html><div id="test-custom-html">Really?</div></html></tip> <tip values="understand"><div id="test-custom-html">Really?</div></tip>
</mcq> </mcq>
<mcq name="mcq_1_2" type="rating" low="Not good at all" high="Extremely good"> <rating name="mcq_1_2" low="Not good at all" high="Extremely good" question="How much do you rate this MCQ?" correct_choices="4,5">
<question>How much do you rate this MCQ?</question>
<choice value="notwant">I don't want to rate it</choice> <choice value="notwant">I don't want to rate it</choice>
<tip values="4,5">I love good grades.</tip>
<tip values="1,2,3">Will do better next time...</tip>
<tip values="notwant">Your loss!</tip>
</rating>
<tip display="4,5">I love good grades.</tip> <mrq name="mrq_1_1" question="What do you like in this MRQ?" required_choices="gracefulness,elegance,beauty" message="Thank you for answering!">
<tip reject="1,2,3">Will do better next time...</tip>
<tip reject="notwant">Your loss!</tip>
</mcq>
<mrq name="mrq_1_1" type="choices">
<question>What do you like in this MRQ?</question>
<choice value="elegance">Its elegance</choice> <choice value="elegance">Its elegance</choice>
<choice value="beauty">Its beauty</choice> <choice value="beauty">Its beauty</choice>
<choice value="gracefulness">Its gracefulness</choice> <choice value="gracefulness">Its gracefulness</choice>
<choice value="bugs">Its bugs</choice> <choice value="bugs">Its bugs</choice>
<tip require="gracefulness">This MRQ is indeed very graceful</tip> <tip values="gracefulness">This MRQ is indeed very graceful</tip>
<tip require="elegance,beauty">This is something everyone has to like about this MRQ</tip> <tip values="elegance,beauty">This is something everyone has to like about this MRQ</tip>
<tip reject="bugs">Nah, there isn't any!</tip> <tip values="bugs">Nah, there aren't any!</tip>
<message type="on-submit">Thank you for answering!</message>
</mrq> </mrq>
<message type="completed"> <message type="completed">
......
...@@ -3,18 +3,15 @@ ...@@ -3,18 +3,15 @@
<p>Please answer the questions below.</p> <p>Please answer the questions below.</p>
</html_demo> </html_demo>
<mrq name="mrq_1_1" type="choices"> <mrq name="mrq_1_1" question="What do you like in this MRQ?" message="Thank you for answering!" required_choices="gracefulness,elegance,beauty">
<question>What do you like in this MRQ?</question>
<choice value="elegance">Its elegance</choice> <choice value="elegance">Its elegance</choice>
<choice value="beauty">Its beauty</choice> <choice value="beauty">Its beauty</choice>
<choice value="gracefulness">Its gracefulness</choice> <choice value="gracefulness">Its gracefulness</choice>
<choice value="bugs">Its bugs</choice> <choice value="bugs">Its bugs</choice>
<tip require="gracefulness">This MRQ is indeed very graceful</tip> <tip values="gracefulness">This MRQ is indeed very graceful</tip>
<tip require="elegance,beauty">This is something everyone has to like about this MRQ</tip> <tip values="elegance,beauty">This is something everyone has to like about this MRQ</tip>
<tip reject="bugs">Nah, there isn't any!</tip> <tip values="bugs">Nah, there aren't any!</tip>
<message type="on-submit">Thank you for answering!</message>
</mrq> </mrq>
<message type="completed"> <message type="completed">
......
...@@ -91,9 +91,9 @@ class MentoringAssessmentTest(MentoringBaseTest): ...@@ -91,9 +91,9 @@ class MentoringAssessmentTest(MentoringBaseTest):
@staticmethod @staticmethod
def question_text(number): def question_text(number):
if number: if number:
return "QUESTION %s" % number return "Question %s" % number
else: else:
return "QUESTION" return "Question"
def freeform_answer(self, number, mentoring, controls, text_input, result, saved_value="", last=False): def freeform_answer(self, number, mentoring, controls, text_input, result, saved_value="", last=False):
question = self.expect_question_visible(number, mentoring) question = self.expect_question_visible(number, mentoring)
......
...@@ -68,8 +68,8 @@ class MCQBlockTest(MentoringBaseTest): ...@@ -68,8 +68,8 @@ class MCQBlockTest(MentoringBaseTest):
mcq1_legend = mcq1.find_element_by_css_selector('legend') mcq1_legend = mcq1.find_element_by_css_selector('legend')
mcq2_legend = mcq2.find_element_by_css_selector('legend') mcq2_legend = mcq2.find_element_by_css_selector('legend')
self.assertEqual(mcq1_legend.text, 'QUESTION 1\nDo you like this MCQ?') self.assertEqual(mcq1_legend.text, 'Question 1\nDo you like this MCQ?')
self.assertEqual(mcq2_legend.text, 'QUESTION 2\nHow much do you rate this MCQ?') self.assertEqual(mcq2_legend.text, 'Question 2\nHow do you rate this MCQ?')
mcq1_choices = mcq1.find_elements_by_css_selector('.choices .choice label') mcq1_choices = mcq1.find_elements_by_css_selector('.choices .choice label')
mcq2_choices = mcq2.find_elements_by_css_selector('.rating .choice label') mcq2_choices = mcq2.find_elements_by_css_selector('.rating .choice label')
...@@ -144,7 +144,7 @@ class MCQBlockTest(MentoringBaseTest): ...@@ -144,7 +144,7 @@ class MCQBlockTest(MentoringBaseTest):
self.assertFalse(submit.is_enabled()) self.assertFalse(submit.is_enabled())
mcq_legend = mcq.find_element_by_css_selector('legend') mcq_legend = mcq.find_element_by_css_selector('legend')
self.assertEqual(mcq_legend.text, 'QUESTION\nWhat do you like in this MRQ?') self.assertEqual(mcq_legend.text, 'Question\nWhat do you like in this MRQ?')
mcq_choices = mcq.find_elements_by_css_selector('.choices .choice label') mcq_choices = mcq.find_elements_by_css_selector('.choices .choice label')
...@@ -195,7 +195,7 @@ class MCQBlockTest(MentoringBaseTest): ...@@ -195,7 +195,7 @@ class MCQBlockTest(MentoringBaseTest):
# this could be a list comprehension, but a bit complicated one - hence explicit loop # this could be a list comprehension, but a bit complicated one - hence explicit loop
for choice_wrapper in questionnaire.find_elements_by_css_selector(".choice"): for choice_wrapper in questionnaire.find_elements_by_css_selector(".choice"):
choice_label = choice_wrapper.find_element_by_css_selector("label .choice-text") choice_label = choice_wrapper.find_element_by_css_selector("label .choice-text")
result.append(choice_label.find_element_by_css_selector("div.html_child").get_attribute('innerHTML')) result.append(choice_label.get_attribute('innerHTML'))
return result return result
......
...@@ -5,39 +5,34 @@ ...@@ -5,39 +5,34 @@
<p>Please answer the questions below.</p> <p>Please answer the questions below.</p>
</html_demo> </html_demo>
<answer name="goal"> <answer name="goal" question="What is your goal?" />
<question>What is your goal?</question>
</answer>
<mcq name="mcq_1_1" type="choices"> <mcq name="mcq_1_1" question="Do you like this MCQ?" correct_choices="yes">
<question>Do you like this MCQ?</question>
<choice value="yes">Yes</choice> <choice value="yes">Yes</choice>
<choice value="maybenot">Maybe not</choice> <choice value="maybenot">Maybe not</choice>
<choice value="understand">I don't understand</choice> <choice value="understand">I don't understand</choice>
<tip display="yes">Great!</tip> <tip values="yes">Great!</tip>
<tip reject="maybenot">Ah, damn.</tip> <tip values="maybenot">Ah, damn.</tip>
<tip reject="understand"><html><div id="test-custom-html">Really?</div></html></tip> <tip values="understand"><div id="test-custom-html">Really?</div></tip>
</mcq> </mcq>
<mcq name="mcq_1_2" type="rating" low="Not good at all" high="Extremely good"> <rating name="mcq_1_2" low="Not good at all" high="Extremely good" question="How much do you rate this MCQ?" correct_choices="4,5">
<question>How much do you rate this MCQ?</question>
<choice value="notwant">I don't want to rate it</choice> <choice value="notwant">I don't want to rate it</choice>
<tip display="4,5">I love good grades.</tip> <tip values="4,5">I love good grades.</tip>
<tip reject="1,2,3">Will do better next time...</tip> <tip values="1,2,3">Will do better next time...</tip>
<tip reject="notwant">Your loss!</tip> <tip values="notwant">Your loss!</tip>
</mcq> </rating>
<mrq name="mrq_1_1" type="choices"> <mrq name="mrq_1_1" question="What do you like in this MRQ?" required_choices="gracefulness,elegance,beauty">
<question>What do you like in this MRQ?</question>
<choice value="elegance">Its elegance</choice> <choice value="elegance">Its elegance</choice>
<choice value="beauty">Its beauty</choice> <choice value="beauty">Its beauty</choice>
<choice value="gracefulness">Its gracefulness</choice> <choice value="gracefulness">Its gracefulness</choice>
<choice value="bugs">Its bugs</choice> <choice value="bugs">Its bugs</choice>
<tip require="gracefulness">This MRQ is indeed very graceful</tip> <tip values="gracefulness">This MRQ is indeed very graceful</tip>
<tip require="elegance,beauty">This is something everyone has to like about this MRQ</tip> <tip values="elegance,beauty">This is something everyone has to like about this MRQ</tip>
<tip reject="bugs">Nah, there isn't any!</tip> <tip values="bugs">Nah, there isn't any!</tip>
</mrq> </mrq>
</mentoring> </mentoring>
...@@ -4,14 +4,13 @@ ...@@ -4,14 +4,13 @@
<p>Please answer the questions below.</p> <p>Please answer the questions below.</p>
</html_demo> </html_demo>
<mcq name="mcq_1_1" type="choices"> <mcq name="mcq_1_1" question="Do you like this MCQ?" correct_choices="yes">
<question>Do you like this MCQ?</question>
<choice value="yes">Yes</choice> <choice value="yes">Yes</choice>
<choice value="maybenot">Maybe not</choice> <choice value="maybenot">Maybe not</choice>
<choice value="understand">I don't understand</choice> <choice value="understand">I don't understand</choice>
<tip display="yes">Great!</tip> <tip values="yes">Great!</tip>
<tip reject="maybenot">Ah, damn.</tip> <tip values="maybenot">Ah, damn.</tip>
<tip reject="understand"><html><div id="test-custom-html">Really?</div></html></tip> <tip values="understand"><div id="test-custom-html">Really?</div></tip>
</mcq> </mcq>
</mentoring> </mentoring>
<vertical_demo> <vertical_demo>
<mentoring url_name="mcq_1" enforce_dependency="false"> <mentoring url_name="mcq_1" enforce_dependency="false">
<mcq name="mcq_1_1" type="choices"> <mcq name="mcq_1_1" question="Do you like this MCQ?" correct_choices="yes">
<question>Do you like this MCQ?</question>
<choice value="yes">Yes</choice> <choice value="yes">Yes</choice>
<choice value="maybenot">Maybe not</choice> <choice value="maybenot">Maybe not</choice>
<choice value="understand">I don't understand</choice> <choice value="understand">I don't understand</choice>
<tip display="yes">Great!</tip> <tip values="yes">Great!</tip>
<tip reject="maybenot">Ah, damn.</tip> <tip values="maybenot">Ah, damn.</tip>
<tip reject="understand"><html><div id="test-custom-html">Really?</div></html></tip> <tip values="understand"><div id="test-custom-html">Really?</div></tip>
</mcq> </mcq>
<mcq name="mcq_1_2" type="rating" low="Not good at all" high="Extremely good"> <rating name="mcq_1_2" low="Not good at all" high="Extremely good" question="How do you rate this MCQ?" correct_choices="4,5">
<question>How much do you rate this MCQ?</question>
<choice value="notwant">I don't want to rate it</choice> <choice value="notwant">I don't want to rate it</choice>
<tip display="4,5">I love good grades.</tip> <tip values="4,5">I love good grades.</tip>
<tip reject="1,2,3">Will do better next time...</tip> <tip values="1,2,3">Will do better next time...</tip>
<tip reject="notwant">Your loss!</tip> <tip values="notwant">Your loss!</tip>
</mcq> </rating>
<message type="completed"> <message type="completed">
All is good now... All is good now...
<html><p>Congratulations!</p></html> <p>Congratulations!</p>
</message> </message>
</mentoring> </mentoring>
</vertical_demo> </vertical_demo>
<vertical_demo> <vertical_demo>
<mentoring url_name="mcq_with_comments" display_name="MRQ With Resizable popups" weight="1" enforce_dependency="false"> <mentoring url_name="mcq_with_comments" display_name="MRQ With Resizable popups" weight="1" enforce_dependency="false">
<mrq name="mrq_1_1_7" type="choices"> <mrq name="mrq_1_1_7" question="What do you like in this MRQ?" required_choices="elegance,gracefulness,beauty">
<question>What do you like in this MRQ?</question>
<choice value="elegance">Its elegance</choice> <choice value="elegance">Its elegance</choice>
<choice value="beauty">Its beauty</choice> <choice value="beauty">Its beauty</choice>
<choice value="gracefulness">Its gracefulness</choice> <choice value="gracefulness">Its gracefulness</choice>
<choice value="bugs">Its bugs</choice> <choice value="bugs">Its bugs</choice>
<tip require="gracefulness" width ="200" height = "200">This MRQ is indeed very graceful</tip> <tip values="gracefulness" width ="200" height = "200">This MRQ is indeed very graceful</tip>
<tip require="elegance" width ="600" height = "800">This is something everyone has to like about this MRQ</tip> <tip values="elegance" width ="600" height = "800">This is something everyone has to like about this MRQ</tip>
<tip require="beauty" width ="400" height = "600">This is something everyone has to like about beauty</tip> <tip values="beauty" width ="400" height = "600">This is something everyone has to like about beauty</tip>
<tip reject="bugs" width = "100" height = "200">Nah, there isn\'t any!</tip> <tip values="bugs" width = "100" height = "200">Nah, there isn\'t any!</tip>
<!--<message type="on-submit">Thank you for answering!</message> --> <!--<message type="on-submit">This is deliberately commented out to test parsing of XML comments</message> -->
</mrq> </mrq>
<message type="completed"> <message type="completed">
<html><p>Congratulations!</p></html> <p>Congratulations!</p>
</message> </message>
<message type="incomplete"> <message type="incomplete">
<html><p>Still some work to do...</p></html> <p>Still some work to do...</p>
</message> </message>
</mentoring> </mentoring>
</vertical_demo> </vertical_demo>
<vertical_demo> <vertical_demo>
<mentoring url_name="mcq_with_comments" display_name="MCQ With Resizable popups" weight="1" enforce_dependency="false"> <mentoring url_name="mcq_with_comments" display_name="MCQ With Resizable popups" weight="1" enforce_dependency="false">
<mcq name="mrq_1_1_7" type="choices"> <mcq name="mrq_1_1_7" question="What do you like in this MCQ?" correct_choices="gracefulness,elegance,beauty">
<question>What do you like in this MRQ?</question> <choice value="elegance"><b>Its elegance</b></choice>
<choice value="elegance"><html><b>Its elegance</b></html></choice> <choice value="beauty"><i>Its beauty</i></choice>
<choice value="beauty"><html><i>Its beauty</i></html></choice> <choice value="gracefulness"><strong>Its gracefulness</strong></choice>
<choice value="gracefulness"><html><strong>Its gracefulness</strong></html></choice> <choice value="bugs"><span style="font-color:red">Its bugs</span></choice>
<choice value="bugs"><html><span style="font-color:red">Its bugs</span></html></choice>
<tip require="gracefulness" width ="200" height = "200">This MRQ is indeed very graceful</tip> <tip values="gracefulness" width ="200" height = "200">This MCQ is indeed very graceful</tip>
<tip require="elegance" width ="600" height = "800">This is something everyone has to like about this MRQ</tip> <tip values="elegance" width ="600" height = "800">This is something everyone has to like about this MCQ</tip>
<tip require="beauty" width ="400" height = "600">This is something everyone has to like about beauty</tip> <tip values="beauty" width ="400" height = "600">This is something everyone has to like about beauty</tip>
<tip reject="bugs" width = "100" height = "200">Nah, there isn\'t any!</tip> <tip values="bugs" width = "100" height = "200">Nah, there isn\'t any!</tip>
</mcq> </mcq>
<message type="completed"> <message type="completed">
<html><p>Congratulations!</p></html> <p>Congratulations!</p>
</message> </message>
<message type="incomplete"> <message type="incomplete">
<html><p>Still some work to do...</p></html> <p>Still some work to do...</p>
</message> </message>
</mentoring> </mentoring>
</vertical_demo> </vertical_demo>
<vertical_demo> <vertical_demo>
<mentoring url_name="mcq_with_comments" display_name="MRQ With Resizable popups" weight="1" enforce_dependency="false"> <mentoring url_name="mcq_with_comments" display_name="MRQ With Resizable popups" weight="1" enforce_dependency="false">
<mrq name="mrq_1_1_7" type="choices"> <mrq name="mrq_1_1_7" question="What do you like in this MRQ?" required_choices="elegance,beauty,gracefulness">
<question>What do you like in this MRQ?</question> <choice value="elegance"><b>Its elegance</b></choice>
<choice value="elegance"><html><b>Its elegance</b></html></choice> <choice value="beauty"><i>Its beauty</i></choice>
<choice value="beauty"><html><i>Its beauty</i></html></choice> <choice value="gracefulness"><strong>Its gracefulness</strong></choice>
<choice value="gracefulness"><html><strong>Its gracefulness</strong></html></choice> <choice value="bugs"><span style="font-color:red">Its bugs</span></choice>
<choice value="bugs"><html><span style="font-color:red">Its bugs</span></html></choice>
<tip require="gracefulness" width ="200" height = "200">This MRQ is indeed very graceful</tip> <tip values="gracefulness" width ="200" height = "200">This MRQ is indeed very graceful</tip>
<tip require="elegance" width ="600" height = "800">This is something everyone has to like about this MRQ</tip> <tip values="elegance" width ="600" height = "800">This is something everyone has to like about this MRQ</tip>
<tip require="beauty" width ="400" height = "600">This is something everyone has to like about beauty</tip> <tip values="beauty" width ="400" height = "600">This is something everyone has to like about beauty</tip>
<tip reject="bugs" width = "100" height = "200">Nah, there isn\'t any!</tip> <tip values="bugs" width = "100" height = "200">Nah, there aren\'t any!</tip>
</mrq> </mrq>
<message type="completed"> <message type="completed">
<html><p>Congratulations!</p></html> <p>Congratulations!</p>
</message> </message>
<message type="incomplete"> <message type="incomplete">
<html><p>Still some work to do...</p></html> <p>Still some work to do...</p>
</message> </message>
</mentoring> </mentoring>
</vertical_demo> </vertical_demo>
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