Commit 14fdfb43 by Braden MacDonald

Make Mentoring block itself studio-editable, remove <title> and <header> blocks

parent 00778d03
......@@ -6,5 +6,3 @@ from .mrq import MRQBlock
from .message import MentoringMessageBlock
from .table import MentoringTableBlock, MentoringTableColumnBlock, MentoringTableColumnHeaderBlock
from .tip import TipBlock
from .title import TitleBlock
from .header import SharedHeaderBlock
# -*- coding: utf-8 -*-
#
# Copyright (C) 2014 Harvard
#
# Authors:
# Xavier Antoviaque <xavier@antoviaque.org>
# David Gabor Bodor <david.gabor.bodor@gmail.com>
#
# 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/>.
#
from .html import HTMLBlock
class SharedHeaderBlock(HTMLBlock):
"""
A shared header block shown under the title.
"""
FIXED_CSS_CLASS = "shared-header"
pass
......@@ -22,18 +22,74 @@
#
# Imports ###########################################################
from lxml import etree
from .common import BlockWithContent
from xblock.core import XBlock
from xblock.fields import Scope, String
from xblock.fragment import Fragment
from xblockutils.studio_editable import StudioEditableXBlockMixin
# Classes ###########################################################
class MentoringMessageBlock(BlockWithContent):
class MentoringMessageBlock(XBlock, StudioEditableXBlockMixin):
"""
A message which can be conditionally displayed at the mentoring block level,
for example upon completion of the block
"""
TEMPLATE = 'templates/html/message.html'
content = String(help="Message to display upon completion", scope=Scope.content, default="")
type = String(help="Type of message", scope=Scope.content, default="completed")
content = String(
display_name="Message",
help="Message to display upon completion",
scope=Scope.content,
default="",
multiline_editor="html",
resettable_editor=False,
)
type = String(
help="Type of message",
scope=Scope.content,
default="completed",
values=(
{"display_name": "Completed", "value": "completed"},
{"display_name": "Incompleted", "value": "incomplete"},
{"display_name": "Reached max. # of attemps", "value": "max_attempts_reached"},
),
)
editable_fields = ("content", )
def fallback_view(self, view_name, context):
html = u'<div class="message {msg_type}">{content}</div>'.format(msg_type=self.type, content=self.content)
return Fragment(html)
@property
def display_name(self):
if self.type == 'max_attempts_reached':
max_attempts = self.get_parent().max_attempts
return u"Message when student reaches max. # of attempts ({current_limit})".format(
current_limit=u"unlimited" if max_attempts == 0 else max_attempts
)
if self.type == 'completed':
return u"Message shown when complete"
if self.type == 'incomplete':
return u"Message shown when incomplete"
return u"INVALID MESSAGE"
@classmethod
def get_template(cls, template_id):
"""
Used to interact with Studio's create_xblock method to instantiate pre-defined templates.
"""
return {'metadata': {'type': template_id, 'content': "Message goes here."}, 'data': {}}
@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.content = unicode(node.text or u"")
block.type = node.attrib['type']
for child in node:
block.content += etree.tostring(child, encoding='unicode')
return block
......@@ -17,7 +17,20 @@
# along with this program in a file in the toplevel directory called
# "AGPLv3". If not, see <http://www.gnu.org/licenses/>.
#
from .utils import child_isinstance
from lazy import lazy
from xblockutils.helpers import child_isinstance
def _normalize_id(key):
"""
Helper method to normalize a key to avoid issues where some keys have version/branch and others don't.
e.g. self.scope_ids.usage_id != self.runtime.get_block(self.scope_ids.usage_id).scope_ids.usage_id
"""
if hasattr(key, "for_branch"):
key = key.for_branch(None)
if hasattr(key, "for_version"):
key = key.for_version(None)
return key
class StepParentMixin(object):
......@@ -30,19 +43,31 @@ class StepParentMixin(object):
"""
Get the usage_ids of all of this XBlock's children that are "Steps"
"""
return [child_id for child_id in self.children if child_isinstance(self, child_id, StepMixin)]
return [_normalize_id(child_id) for child_id in self.children if child_isinstance(self, child_id, StepMixin)]
class StepMixin(object):
"""
An XBlock mixin for a child block that is a "Step"
"""
@property
has_author_view = True
@lazy
def step_number(self):
return list(self.get_parent().steps).index(self.scope_ids.usage_id) + 1
return list(self.get_parent().steps).index(_normalize_id(self.scope_ids.usage_id)) + 1
@property
@lazy
def lonely_step(self):
if self.scope_ids.usage_id not in self.get_parent().steps:
if _normalize_id(self.scope_ids.usage_id) not in self.get_parent().steps:
raise ValueError("Step's parent should contain Step", self, self.get_parent().steps)
return len(self.get_parent().steps) == 1
def author_view(self, context):
context = context or {}
context['hide_header'] = True
return self.mentoring_view(context)
def author_preview_view(self, context):
context = context or {}
context['hide_header'] = True
return self.student_view(context)
# -*- coding: utf-8 -*-
#
# Copyright (C) 2014 Harvard
#
# Authors:
# Xavier Antoviaque <xavier@antoviaque.org>
#
# 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
# Classes ###########################################################
class TitleBlock(XBlock):
"""
A simple html representation of a title, with the mentoring weight.
"""
content = String(help="Text to display", scope=Scope.content, default="")
"""
Helper methods
Should eventually be moved to xblock-utils.
"""
def child_isinstance(block, child_id, block_class_or_mixin):
"""
Is "block"'s child identified by usage_id "child_id" an instance of
"block_class_or_mixin"?
This is a bit complicated since it avoids the need to actually
instantiate the child block.
"""
def_id = block.runtime.id_reader.get_definition_id(child_id)
type_name = block.runtime.id_reader.get_block_type(def_id)
child_class = block.runtime.load_block_type(type_name)
return issubclass(child_class, block_class_or_mixin)
......@@ -27,31 +27,30 @@ import logging
from collections import namedtuple
from lxml import etree
from StringIO import StringIO
from xblock.core import XBlock
from xblock.exceptions import NoSuchViewError
from xblock.fields import Boolean, Scope, String, Integer, Float, List
from xblock.fragment import Fragment
from xblock.validation import ValidationMessage
from components import TitleBlock, SharedHeaderBlock, MentoringMessageBlock
from components import MentoringMessageBlock
from components.step import StepParentMixin, StepMixin
from xblockutils.resources import ResourceLoader
from xblockutils.studio_editable import StudioEditableXBlockMixin, StudioContainerXBlockMixin
# Globals ###########################################################
log = logging.getLogger(__name__)
loader = ResourceLoader(__name__)
default_xml_content = loader.render_template('templates/xml/mentoring_default.xml', {})
# Classes ###########################################################
Score = namedtuple("Score", ["raw", "percentage", "correct", "incorrect", "partially_correct"])
class MentoringBlock(XBlock, StepParentMixin):
@XBlock.needs("i18n")
class MentoringBlock(XBlock, StepParentMixin, StudioEditableXBlockMixin, StudioContainerXBlockMixin):
"""
An XBlock providing mentoring capabilities
......@@ -60,14 +59,10 @@ class MentoringBlock(XBlock, StepParentMixin):
student is a) provided mentoring advices and asked to alter his answer, or b) is given the
ok to continue.
"""
@staticmethod
def is_default_xml_content(value):
return value == default_xml_content
# Content
MENTORING_MODES = ('standard', 'assessment')
mode = String(
display_name="Mode",
help="Mode of the mentoring. 'standard' or 'assessment'",
default='standard',
scope=Scope.content,
......@@ -104,7 +99,13 @@ class MentoringBlock(XBlock, StepParentMixin):
scope=Scope.content,
enforce_type=True
)
xml_content = String(help="XML content", default=default_xml_content, scope=Scope.content)
xml_content = String(
help="Not used for version 2. This field is here only to preserve the data needed to upgrade from v1 to v2.",
display_name="XML content",
default='',
scope=Scope.content,
multiline_editor=True
)
# Settings
weight = Float(
......@@ -114,8 +115,8 @@ class MentoringBlock(XBlock, StepParentMixin):
enforce_type=True
)
display_name = String(
help="Display name of the component",
default="Mentoring XBlock",
help="Title to display",
default="Mentoring Questions",
scope=Scope.settings
)
......@@ -155,11 +156,17 @@ class MentoringBlock(XBlock, StepParentMixin):
scope=Scope.preferences
)
editable_fields = (
'mode', 'followed_by', 'max_attempts', 'enforce_dependency',
'display_submit', 'weight', 'display_name',
)
icon_class = 'problem'
has_score = True
has_children = True
FLOATING_BLOCKS = (TitleBlock, MentoringMessageBlock, SharedHeaderBlock)
def _(self, text):
""" translate text """
return self.runtime.service(self, "i18n").ugettext(text)
@property
def is_assessment(self):
......@@ -171,7 +178,7 @@ class MentoringBlock(XBlock, StepParentMixin):
weights = (float(self.runtime.get_block(step_id).weight) for step_id in self.steps)
total_child_weight = sum(weights)
if total_child_weight == 0:
return (0, 0, 0, 0)
return Score(0, 0, 0, 0, 0)
score = sum(r[1]['score'] * r[1]['weight'] for r in self.student_results) / total_child_weight
correct = sum(1 for r in self.student_results if r[1]['status'] == 'correct')
incorrect = sum(1 for r in self.student_results if r[1]['status'] == 'incorrect')
......@@ -184,17 +191,11 @@ class MentoringBlock(XBlock, StepParentMixin):
self.migrate_fields()
fragment = Fragment()
title = u""
header = u""
child_content = u""
for child_id in self.children:
child = self.runtime.get_block(child_id)
if isinstance(child, TitleBlock):
title = child.content
elif isinstance(child, SharedHeaderBlock):
header = child.render('mentoring_view', context).content
elif isinstance(child, MentoringMessageBlock):
if isinstance(child, MentoringMessageBlock):
pass # TODO
else:
child_fragment = child.render('mentoring_view', context)
......@@ -203,8 +204,7 @@ class MentoringBlock(XBlock, StepParentMixin):
fragment.add_content(loader.render_template('templates/html/mentoring.html', {
'self': self,
'title': title,
'header': header,
'title': self.display_name,
'child_content': child_content,
'missing_dependency_url': self.has_missing_dependency and self.next_step_url,
}))
......@@ -243,26 +243,6 @@ class MentoringBlock(XBlock, StepParentMixin):
}
@property
def title(self):
"""
Returns the title child.
"""
for child in self.get_children_objects():
if isinstance(child, TitleBlock):
return child
return None
@property
def header(self):
"""
Return the header child.
"""
for child in self.get_children_objects():
if isinstance(child, SharedHeaderBlock):
return child
return None
@property
def has_missing_dependency(self):
"""
Returns True if the student needs to complete another step before being able to complete
......@@ -375,7 +355,7 @@ class MentoringBlock(XBlock, StepParentMixin):
completed = False
current_child = None
children = [self.runtime.get_block(child_id) for child_id in self.children]
children = [child for child in children if not isinstance(child, self.FLOATING_BLOCKS)]
children = [child for child in children if not isinstance(child, MentoringMessageBlock)]
steps = [child for child in children if isinstance(child, StepMixin)] # Faster than the self.steps property
for child in children:
......@@ -466,72 +446,81 @@ class MentoringBlock(XBlock, StepParentMixin):
html += child.render('mentoring_view', {}).content # TODO: frament_text_rewriting ?
return html
def studio_view(self, context):
def clean_studio_edits(self, data):
"""
Editing view in Studio
Given POST data dictionary 'data', clean the data before validating it.
e.g. fix capitalization, remove trailing spaces, etc.
"""
fragment = Fragment()
fragment.add_content(loader.render_template('templates/html/mentoring_edit.html', {
'self': self,
'xml_content': self.xml_content,
}))
fragment.add_javascript_url(
self.runtime.local_resource_url(self, 'public/js/mentoring_edit.js'))
fragment.add_css_url(
self.runtime.local_resource_url(self, 'public/css/mentoring_edit.css'))
fragment.initialize_js('MentoringEditBlock')
if data.get('mode') == 'assessment' and 'max_attempts' not in data:
# assessment has a default of 2 max_attempts
data['max_attempts'] = 2
def validate(self):
"""
Validates the state of this XBlock except for individual field values.
"""
validation = super(MentoringBlock, self).validate()
a_child_has_issues = False
message_types_present = set()
for child_id in self.children:
child = self.runtime.get_block(child_id)
# Check if the child has any errors:
if not child.validate().empty:
a_child_has_issues = True
# Ensure there is only one "message" block of each type:
if isinstance(child, MentoringMessageBlock):
msg_type = child.type
if msg_type in message_types_present:
validation.add(ValidationMessage(
ValidationMessage.ERROR,
self._(u"There should only be one '{}' message component.".format(msg_type))
))
message_types_present.add(msg_type)
if a_child_has_issues:
validation.add(ValidationMessage(
ValidationMessage.ERROR,
self._(u"A component inside this mentoring block has issues.")
))
return validation
def author_preview_view(self, context):
"""
Child blocks can override this to add a custom preview shown to authors in Studio when
not editing this block's children.
"""
fragment = self.student_view(context)
fragment.add_content(loader.render_template('templates/html/mentoring_url_name.html', {
"url_name": self.url_name
}))
fragment.add_css_url(self.runtime.local_resource_url(self, 'public/css/mentoring_edit.css'))
return fragment
@XBlock.json_handler
def studio_submit(self, submissions, suffix=''):
log.info(u'Received studio submissions: {}'.format(submissions))
success = True
xml_content = submissions['xml_content']
try:
content = etree.parse(StringIO(xml_content))
except etree.XMLSyntaxError as e:
response = {
'result': 'error',
'message': e.message
}
success = False
else:
root = content.getroot()
if 'mode' in root.attrib:
if root.attrib['mode'] not in self.MENTORING_MODES:
response = {
'result': 'error',
'message': "Invalid mentoring mode: should be 'standard' or 'assessment'"
}
success = False
elif root.attrib['mode'] == 'assessment' and 'max_attempts' not in root.attrib:
# assessment has a default of 2 max_attempts
root.attrib['max_attempts'] = '2'
if success:
response = {
'result': 'success',
}
self.xml_content = etree.tostring(content, pretty_print=True)
log.debug(u'Response from Studio: {}'.format(response))
return response
def author_edit_view(self, context):
"""
Add some HTML to the author view that allows authors to add child blocks.
"""
fragment = super(MentoringBlock, self).author_edit_view(context)
fragment.add_content(loader.render_template('templates/html/mentoring_add_buttons.html', {}))
fragment.add_css_url(self.runtime.local_resource_url(self, 'public/css/mentoring_edit.css'))
fragment.add_javascript_url(self.runtime.local_resource_url(self, 'public/js/mentoring_edit.js'))
fragment.initialize_js('MentoringEditComponents')
return fragment
@property
def url_name_with_default(self):
@classmethod
def parse_xml(cls, node, runtime, keys, id_generator):
"""
Ensure the `url_name` is set to a unique, non-empty value.
In future once hte pinned version of XBlock is updated,
we can remove this and change the field to use the
xblock.fields.UNIQUE_ID flag instead.
To avoid collisions with e.g. the existing <html> XBlock in edx-platform,
we prefix all of the mentoring block tags with "mentoring-". However,
using that prefix in the XML is optional. This method adds that prefix
in when parsing XML in a mentoring context.
"""
if self.url_name == 'mentoring-default':
return self.scope_ids.usage_id
else:
return self.url_name
PREFIX_TAGS = ("answer", "answer-recap", "quizz", "mcq", "mrq", "rating", "message", "tip", "choice", "column")
for element in node.iter():
# We have prefixed all our XBlock entry points with "mentoring-". But using the "mentoring-"
# prefix in the XML is optional:
if element.tag in PREFIX_TAGS:
element.tag = "mentoring-{}".format(element.tag)
return super(MentoringBlock, cls).parse_xml(node, runtime, keys, id_generator)
@staticmethod
def workbench_scenarios():
......
.mentoring-edit .module-actions .error-message {
color: red;
/* Custom appearance for our "Add" buttons */
.xblock[data-block-type=mentoring] .add-xblock-component .new-component .new-component-type .add-xblock-component-button {
width: 200px;
height: 30px;
line-height: 30px;
}
.xblock[data-block-type=mentoring] .add-xblock-component .new-component .new-component-type .add-xblock-component-button.disabled,
.xblock[data-block-type=mentoring] .add-xblock-component .new-component .new-component-type .add-xblock-component-button.disabled:hover {
background-color: #ccc;
border-color: #888;
cursor: default;
}
function MentoringEditBlock(runtime, element) {
var xmlEditorTextarea = $('.block-xml-editor', element),
xmlEditor = CodeMirror.fromTextArea(xmlEditorTextarea[0], { mode: 'xml' });
$('.save-button', element).bind('click', function() {
var handlerUrl = runtime.handlerUrl(element, 'studio_submit'),
data = {
'xml_content': xmlEditor.getValue(),
};
$('.error-message', element).html();
$.post(handlerUrl, JSON.stringify(data)).done(function(response) {
if (response.result === 'success') {
window.location.reload(false);
} else {
$('.error-message', element).html('Error: '+response.message);
}
function MentoringEditComponents(runtime, element) {
"use strict";
// Disable "add" buttons when a message of that type already exists:
var $buttons = $('.add-xblock-component-button[data-category=mentoring-message]', element);
var updateButtons = function() {
$buttons.each(function() {
var msg_type = $(this).data('boilerplate');
$(this).toggleClass('disabled', $('.xblock .message.'+msg_type).length > 0);
});
};
updateButtons();
$buttons.click(function(ev) {
if ($(this).is('.disabled')) {
ev.preventDefault();
ev.stopPropagation();
} else {
$(this).addClass('disabled');
}
});
$('.cancel-button', element).bind('click', function() {
runtime.notify('cancel', {});
});
runtime.listenTo('deleted-child', updateButtons);
}
......@@ -4,10 +4,9 @@
attempting this step.
</div>
{% if title or header %}
{% if title %}
<div class="title">
{% if title %} <h2>{{ title }}</h2> {% endif %}
{% if header %} {{ header|safe }} {% endif %}
</div>
{% endif %}
......
{% load i18n %}
<div class="add-xblock-component new-component-item adding">
<div class="new-component">
<h5>Add New Component</h5>
<ul class="new-component-type">
<li><a href="#" class="single-template add-xblock-component-button" data-category="mentoring-answer">Long Answer</a></li>
<li><a href="#" class="single-template add-xblock-component-button" data-category="mentoring-mcq">Multiple Choice Question</a></li>
<li><a href="#" class="single-template add-xblock-component-button" data-category="mentoring-rating">Rating Question</a></li>
<li><a href="#" class="single-template add-xblock-component-button" data-category="mentoring-mrq">Multiple Response Question</a></li>
<li><a href="#" class="single-template add-xblock-component-button" data-category="html">HTML</a></li>
<li><a href="#" class="single-template add-xblock-component-button" data-category="mentoring-answer-recap">Long Answer Recap</a></li>
<li><a href="#" class="single-template add-xblock-component-button" data-category="mentoring-table">Answer Recap Table</a></li>
<li><a href="#" class="single-template add-xblock-component-button" data-category="mentoring-message" data-boilerplate="completed">Message (Complete)</a></li>
<li><a href="#" class="single-template add-xblock-component-button" data-category="mentoring-message" data-boilerplate="incomplete">Message (Incomplete)</a></li>
<li><a href="#" class="single-template add-xblock-component-button" data-category="mentoring-message" data-boilerplate="max_attempts_reached">Message (Max # Attempts)</a></li>
</ul>
</div>
</div>
{% load i18n %}
<!-- TODO: Replace by default edit view once available in Studio -->
<div class="mentoring-edit editor-with-buttons">
<div class="wrapper-comp-settings is-active" id="settings-tab">
<textarea class="block-xml-editor">{{ xml_content }}</textarea>
</div>
<div class="xblock-actions">
<span class="error-message"></span>
<ul>
<li class="action-item">
<a href="#" class="button action-primary save-button">{% trans "Save" %}</a>
</li>
<li class="action-item">
<a href="#" class="button cancel-button">{% trans "Cancel" %}</a>
</li>
</ul>
</div>
</div>
<mentoring display_name="Nav tooltip title" weight="1" mode="assessment" max_attempts="10">
<title>Default Title</title>
<shared-header>
<mentoring display_name="Mentoring Assessment Example" weight="1" mode="assessment" max_attempts="10">
<html_demo>
<p>This paragraph is shared between <strong>all</strong> questions.</p>
</shared-header>
<html>
<p>Please answer the questions below.</p>
</html>
</html_demo>
<answer name="goal">
<question>What is your goal?</question>
......
......@@ -4,20 +4,18 @@
should be able to export their data as CSV.</p>
</html>
<mentoring display_name="Mentoring Block 1" mode="standard">
<title>Mentoring Block 1</title>
<html>
<html_demo>
<p>Please answer the question below.</p>
</html>
</html_demo>
<answer name="goal">
<question>What is your goal?</question>
</answer>
</mentoring>
<mentoring display_name="Mentoring Block 1" mode="assessment">
<title>Mentoring Block 2 (Assessment)</title>
<html>
<mentoring display_name="Mentoring Block 2 (Assessment)" mode="assessment">
<html_demo>
<p>Please answer the question below.</p>
</html>
</html_demo>
<answer name="inspired">
<question>Who has inspired you the most?</question>
......
<mentoring display_name="Nav tooltip title" weight="1" mode="standard">
<title>Default Title</title>
<html>
<mentoring display_name="Default Title" weight="1" mode="standard">
<html_demo>
<p>Please answer the questions below.</p>
</html>
</html_demo>
<answer name="goal">
<question>What is your goal?</question>
......
<mentoring display_name="Nav tooltip title" weight="1" mode="standard">
<title>Default Title</title>
<html>
<mentoring display_name="Default Title" weight="1" mode="standard">
<html_demo>
<p>Please answer the questions below.</p>
</html>
</html_demo>
<mrq name="mrq_1_1" type="choices">
<question>What do you like in this MRQ?</question>
......
<mentoring url_name="mentoring-assessment-1" display_name="Nav tooltip title" weight="1" mode="assessment" max_attempts="2">
<title>A Simple Assessment</title>
<shared-header>
<mentoring url_name="mentoring-assessment-1" display_name="A Simple Assessment" weight="1" mode="assessment" max_attempts="2">
<html_demo>
<p>This paragraph is shared between <strong>all</strong> questions.</p>
</shared-header>
<html>
<p>Please answer the questions below.</p>
</html>
</html_demo>
<answer name="goal">
<question>What is your goal?</question>
......
<mentoring url_name="mentoring-assessment-2" display_name="Nav tooltip title" weight="1" mode="assessment" max_attempts="2">
<title>A Simple Assessment</title>
<shared-header>
<mentoring url_name="mentoring-assessment-2" display_name="A Simple Assessment" weight="1" mode="assessment" max_attempts="2">
<html_demo>
<p>This paragraph is shared between <strong>all</strong> questions.</p>
</shared-header>
<html>
<p>Please answer the questions below.</p>
</html>
</html_demo>
<mcq name="mcq_1_1" type="choices">
<question>Do you like this MCQ?</question>
......
......@@ -13,11 +13,10 @@
<question>What is your goal?</question>
</answer>
</mentoring>
<mentoring display_name="Mentoring Block 1" mode="assessment">
<title>Mentoring Block 2 (Assessment)</title>
<html>
<mentoring display_name="Mentoring Block 2 (Assessment)" mode="assessment">
<html_demo>
<p>Please answer the question below.</p>
</html>
</html_demo>
<answer name="inspired">
<question>Who has inspired you the most?</question>
......
<vertical_demo>
<mentoring url_name="mcq_with_comments" display_name="MRQ Exercise 7" weight="1" enforce_dependency="false">
<title>MRQ With Resizable popups</title>
<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">
<question>What do you like in this MRQ?</question>
<choice value="elegance">Its elegance</choice>
......
<vertical_demo>
<mentoring url_name="mcq_with_comments" display_name="MCQ Exercise 7" weight="1" enforce_dependency="false">
<title>MRQ With Resizable popups</title>
<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">
<question>What do you like in this MRQ?</question>
<choice value="elegance"><html><b>Its elegance</b></html></choice>
......
<vertical_demo>
<mentoring url_name="mcq_with_comments" display_name="MRQ Exercise 7" weight="1" enforce_dependency="false">
<title>MRQ With Resizable popups</title>
<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">
<question>What do you like in this MRQ?</question>
<choice value="elegance"><html><b>Its elegance</b></html></choice>
......
......@@ -47,18 +47,16 @@ BLOCKS = [
'mentoring-dataexport = mentoring:MentoringDataExportBlock',
'mentoring-table = mentoring.components:MentoringTableBlock',
'column = mentoring.components:MentoringTableColumnBlock',
'header = mentoring.components:MentoringTableColumnHeaderBlock',
'answer = mentoring.components:AnswerBlock',
'quizz = mentoring.components:MCQBlock',
'mcq = mentoring.components:MCQBlock',
'mrq = mentoring.components:MRQBlock',
'message = mentoring.components:MentoringMessageBlock',
'tip = mentoring.components:TipBlock',
'choice = mentoring.components:ChoiceBlock',
'html = mentoring.components:HTMLBlock',
'title = mentoring.components:TitleBlock',
'shared-header = mentoring.components:SharedHeaderBlock',
'mentoring-column = mentoring.components:MentoringTableColumn',
'mentoring-answer = mentoring.components:AnswerBlock',
'mentoring-answer-recap = mentoring.components:AnswerRecapBlock',
'mentoring-mcq = mentoring.components:MCQBlock',
'mentoring-rating = mentoring.components:RatingBlock',
'mentoring-mrq = mentoring.components:MRQBlock',
'mentoring-message = mentoring.components:MentoringMessageBlock',
'mentoring-tip = mentoring.components:TipBlock',
'mentoring-choice = mentoring.components:ChoiceBlock',
]
setup(
......
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