Commit 22e12f6d by Jonathan Piacenti

Implemented extended feedback.

parent 29003051
......@@ -179,7 +179,8 @@ class AnswerBlock(AnswerMixin, StepMixin, StudioEditableXBlockMixin, XBlock):
""" Normal view of this XBlock, identical to mentoring_view """
return self.mentoring_view(context)
def get_results(self):
def get_results(self, previous_response=None):
# Previous result is actually stored in database table-- ignore.
return {
'student_input': self.student_input,
'status': self.status,
......
......@@ -81,10 +81,12 @@ class MCQBlock(SubmittingXBlockMixin, QuestionnaireAbstractBlock):
if submission in tip.values:
tips_html.append(tip.render('mentoring_view').content)
formatted_tips = None
if tips_html:
formatted_tips = loader.render_template('templates/html/tip_choice_group.html', {
'tips_html': tips_html,
})
})
self.student_choice = submission
......@@ -95,13 +97,13 @@ class MCQBlock(SubmittingXBlockMixin, QuestionnaireAbstractBlock):
return {
'submission': submission,
'status': 'correct' if correct else 'incorrect',
'tips': formatted_tips if tips_html else None,
'tips': formatted_tips,
'weight': self.weight,
'score': 1 if correct else 0,
}
def get_results(self):
return self.calculate_results(self.student_choice)
def get_results(self, previous_result):
return self.calculate_results(previous_result['submission'])
def submit(self, submission):
log.debug(u'Received MCQ submission: "%s"', submission)
......
......@@ -21,6 +21,7 @@
# Imports ###########################################################
import logging
import json
from collections import namedtuple
......@@ -432,7 +433,7 @@ class MentoringBlock(XBlock, StepParentMixin, StudioEditableXBlockMixin, StudioC
# of the whole mentoring block being completed. This is because in standard mode, all children
# must be correct to complete the block. In assessment mode with extended feedback, completion
# happens when you're out of attempts, no matter how you did.
completed = choices[child.name]['status'] == 'correct'
completed = choices[child.name]['status']
break
# The 'completed' message should always be shown in this case, since no more attempts are available.
......@@ -510,7 +511,7 @@ class MentoringBlock(XBlock, StepParentMixin, StudioEditableXBlockMixin, StudioC
})
return {
'submitResults': submit_results,
'results': submit_results,
'completed': self.completed,
'attempted': self.attempted,
'message': message,
......@@ -526,6 +527,8 @@ class MentoringBlock(XBlock, StepParentMixin, StudioEditableXBlockMixin, StudioC
steps = [child for child in children if isinstance(child, StepMixin)] # Faster than the self.steps property
assessment_message = None
print children
print submissions
for child in children:
if child.name and child.name in submissions:
submission = submissions[child.name]
......@@ -565,6 +568,7 @@ class MentoringBlock(XBlock, StepParentMixin, StudioEditableXBlockMixin, StudioC
self.num_attempts += 1
self.completed = True
print current_child
event_data['exercise_id'] = current_child.name
event_data['num_attempts'] = self.num_attempts
event_data['submitted_answer'] = submissions
......@@ -581,6 +585,10 @@ class MentoringBlock(XBlock, StepParentMixin, StudioEditableXBlockMixin, StudioC
'correct_answer': len(score.correct),
'incorrect_answer': len(score.incorrect),
'partially_correct_answer': len(score.partially_correct),
'correct': self.correct_json(stringify=False),
'incorrect': self.incorrect_json(stringify=False),
'partial': self.partial_json(stringify=False),
'extended_feedback': self.show_extended_feedback() or '',
'assessment_message': assessment_message,
}
......
......@@ -81,9 +81,25 @@ class MRQBlock(QuestionnaireAbstractBlock):
return self._(u"Ignored")
return self._(u"Not Acceptable")
def get_results(self, previous_result):
"""
Get the results a student has already submitted.
"""
result = self.calculate_results(previous_result['submissions'])
result['completed'] = True
return result
def submit(self, submissions):
log.debug(u'Received MRQ submissions: "%s"', submissions)
result = self.calculate_results(submissions)
self.student_choices = submissions
log.debug(u'MRQ submissions result: %s', result)
return result
def calculate_results(self, submissions):
score = 0
results = []
for choice in self.custom_choices:
choice_completed = True
......@@ -126,18 +142,6 @@ class MRQBlock(QuestionnaireAbstractBlock):
'score': (float(score) / len(results)) if results else 0,
}
def get_results(self):
return self.calculate_results(self.student_choices)
def submit(self, submissions):
log.debug(u'Received MRQ submissions: "%s"', submissions)
result = self.calculate_results(submissions)
self.student_choices = submissions
log.debug(u'MRQ submissions result: %s', result)
return result
def validate_field_data(self, validation, data):
"""
Validate this block's field data.
......
......@@ -136,3 +136,28 @@
.mentoring input[type="radio"] {
margin: 0;
}
.mentoring .review-list {
list-style: none;
padding-left: 0 !important;
margin-left: 0;
}
.mentoring .review-list li {
display: inline;
}
.mentoring .review-list li a{
font-weight: bold;
}
.mentoring .results-section {
float: left;
}
.mentoring .clear {
display: block;
clear: both;
}
.mentoring .review-link {
float: right;
}
\ No newline at end of file
......@@ -17,15 +17,22 @@ function AnswerBlock(runtime, element) {
return $(':input', element).serializeArray();
},
handleReview: function(result) {
$('textarea', element).prop('disabled', true);
},
handleSubmit: function(result) {
if (this.mode === 'assessment')
return;
var checkmark = $('.answer-checkmark', element);
$(element).find('.message').text((result || {}).error || '');
this.clearResult();
if (this.mode === 'assessment') {
// Display of checkmark would be redundant.
return
}
if (result.status === "correct") {
checkmark.addClass('checkmark-correct icon-ok fa-check');
}
......
......@@ -60,7 +60,7 @@ function MentoringBlock(runtime, element) {
if (typeof obj !== 'undefined' && typeof obj[fn] == 'function') {
return obj[fn].apply(obj, Array.prototype.slice.call(arguments, 2));
} else {
return undefined;
return null;
}
}
......
function MentoringAssessmentView(runtime, element, mentoring) {
var gradeTemplate = _.template($('#xblock-grade-template').html());
var reviewQuestionsTemplate = _.template($('#xblock-review-questions-template').html());
var submitDOM, nextDOM, reviewDOM, tryAgainDOM, messagesDOM;
var submitDOM, nextDOM, reviewDOM, tryAgainDOM, messagesDOM, reviewLinkDOM;
var submitXHR;
var checkmark;
var active_child;
......@@ -33,11 +33,11 @@ function MentoringAssessmentView(runtime, element, mentoring) {
function renderGrade() {
notify('navigation', {state: 'unlock'})
var data = $('.grade', element).data();
data.enable_extended = (no_more_attempts() && data.extended_feedback);
_.extend(data, {
'enable_extended': (no_more_attempts() && data.extended_feedback),
'runDetails': function(label) {
if (! this.enable_extended) {
return '.'
if (! data.enable_extended) {
return ''
}
var self = this;
return reviewQuestionsTemplate({'questions': self[label], 'label': label})
......@@ -45,8 +45,13 @@ function MentoringAssessmentView(runtime, element, mentoring) {
});
cleanAll();
$('.grade', element).html(gradeTemplate(data));
reviewLinkDOM.hide();
reviewDOM.hide();
submitDOM.hide();
if (data.enable_extended) {
nextDOM.unbind('click');
nextDOM.bind('click', reviewNextChild)
}
nextDOM.hide();
tryAgainDOM.show();
......@@ -63,6 +68,7 @@ function MentoringAssessmentView(runtime, element, mentoring) {
mentoring.setContent(messagesDOM, data.assessment_message);
messagesDOM.show();
}
$('a.question-link', element).click(reviewJump);
}
function handleTryAgain(result) {
......@@ -80,7 +86,6 @@ function MentoringAssessmentView(runtime, element, mentoring) {
}
function tryAgain() {
var success = true;
var handlerUrl = runtime.handlerUrl(element, 'try_again');
if (submitXHR) {
submitXHR.abort();
......@@ -94,18 +99,26 @@ function MentoringAssessmentView(runtime, element, mentoring) {
nextDOM = $(element).find('.submit .input-next');
reviewDOM = $(element).find('.submit .input-review');
tryAgainDOM = $(element).find('.submit .input-try-again');
reviewLinkDOM = $(element).find('.review-link');
checkmark = $('.assessment-checkmark', element);
messagesDOM = $('.assessment-messages', element);
reviewLinkDOM.hide();
submitDOM.show();
submitDOM.bind('click', submit);
nextDOM.bind('click', displayNextChild);
nextDOM.show();
reviewDOM.bind('click', renderGrade);
tryAgainDOM.bind('click', tryAgain);
active_child = mentoring.step;
function renderGradeEvent(event) {
event.preventDefault();
renderGrade();
}
reviewLinkDOM.bind('click', renderGradeEvent);
reviewDOM.bind('click', renderGradeEvent);
var options = {
onChange: onChange
};
......@@ -135,24 +148,85 @@ function MentoringAssessmentView(runtime, element, mentoring) {
}
}
function displayNextChild() {
cleanAll();
function reviewJump(event) {
// Used only during extended feedback. Assumes completion and attempts exhausted.
event.preventDefault();
// find the next real child block to display. HTMLBlock are always displayed
active_child++;
var target = parseInt($(event.target).data('step')) - 1;
reviewDisplayChild(target);
}
function reviewDisplayChild(child_index) {
active_child = child_index;
cleanAll();
var child = mentoring.steps[active_child];
$(child.element).show();
$(child.element).find("input, textarea").first().focus();
mentoring.publish_event({
event_type: 'xblock.problem_builder.assessment.shown',
exercise_id: child.name
event_type: 'xblock.mentoring.assessment.review',
exercise_id: $(mentoring.steps[active_child]).attr('name')
});
post_display(true);
get_results();
}
if (isDone())
function reviewNextChild() {
nextDOM.attr('disabled', 'disabled');
nextDOM.hide();
findNextChild();
reviewDisplayChild(active_child)
}
function displayNextChild() {
cleanAll();
findNextChild(true);
// find the next real child block to display. HTMLBlock are always displayed
if (isDone()) {
renderGrade();
} else {
post_display();
}
}
function findNextChild(fire_event) {
// find the next real child block to display. HTMLBlock are always displayed
++active_child;
var child = mentoring.steps[active_child];
$(child.element).show();
$(child.element).find("input, textarea").first().focus();
if (fire_event) {
mentoring.publish_event({
event_type: 'xblock.problem_builder.assessment.shown',
exercise_id: child.name
});
}
}
function post_display(show_link) {
nextDOM.attr('disabled', 'disabled');
reviewDOM.attr('disabled', 'disabled');
validateXBlock();
if (no_more_attempts()) {
if (show_link) {
reviewLinkDOM.show();
} else {
reviewDOM.show();
reviewDOM.removeAttr('disabled')
}
} else {
reviewDOM.attr('disabled', 'disabled');
}
validateXBlock(show_link);
if (show_link && ! isLastChild()) {
// User should also be able to browse forward if we're showing the review link.
nextDOM.show();
nextDOM.removeAttr('disabled');
}
if (show_link) {
// The user has no more tries, so the try again button is noise. A disabled submit button
// emphasizes that the user cannot change their answer.
tryAgainDOM.hide();
submitDOM.show();
submitDOM.attr('disabled', 'disabled')
}
}
function onChange() {
......@@ -164,20 +238,20 @@ function MentoringAssessmentView(runtime, element, mentoring) {
}
}
function handleSubmitResults(result) {
$('.grade', element).data('score', result.score);
$('.grade', element).data('correct_answer', result.correct_answer);
$('.grade', element).data('incorrect_answer', result.incorrect_answer);
$('.grade', element).data('partially_correct_answer', result.partially_correct_answer);
$('.grade', element).data('max_attempts', result.max_attempts);
$('.grade', element).data('num_attempts', result.num_attempts);
$('.grade', element).data('assessment_message', result.assessment_message);
$('.attempts', element).data('max_attempts', result.max_attempts);
$('.attempts', element).data('num_attempts', result.num_attempts);
if (result.completed === 'partial') {
function handleResults(response) {
$('.grade', element).data('score', response.score);
$('.grade', element).data('correct_answer', response.correct_answer);
$('.grade', element).data('incorrect_answer', response.incorrect_answer);
$('.grade', element).data('partially_correct_answer', response.partially_correct_answer);
$('.grade', element).data('max_attempts', response.max_attempts);
$('.grade', element).data('num_attempts', response.num_attempts);
$('.grade', element).data('assessment_message', response.assessment_message);
$('.attempts', element).data('max_attempts', response.max_attempts);
$('.attempts', element).data('num_attempts', response.num_attempts);
if (response.completed === 'partial') {
checkmark.addClass('checkmark-partially-correct icon-ok fa-check');
} else if (result.completed === 'correct') {
} else if (response.completed === 'correct') {
checkmark.addClass('checkmark-correct icon-ok fa-check');
} else {
checkmark.addClass('checkmark-incorrect icon-exclamation fa-exclamation');
......@@ -185,40 +259,58 @@ function MentoringAssessmentView(runtime, element, mentoring) {
submitDOM.attr('disabled', 'disabled');
/* Something went wrong with student submission, denied next question */
if (result.step != active_child+1) {
active_child = result.step-1;
displayNextChild();
} else {
nextDOM.removeAttr("disabled");
if (nextDOM.is(':visible')) { nextDOM.focus(); }
reviewDOM.removeAttr("disabled");
if (reviewDOM.is(':visible')) { reviewDOM.focus(); }
/* We're not dealing with the current step */
if (response.step != active_child+1) {
return
}
nextDOM.removeAttr("disabled");
reviewDOM.removeAttr("disabled");
if (nextDOM.is(':visible')) { nextDOM.focus(); }
if (reviewDOM.is(':visible')) { reviewDOM.focus(); }
}
function submit() {
var success = true;
function handleReviewResults(response) {
handleResults(response);
var options = {
max_attempts: response.max_attempts,
num_attempts: response.num_attempts
};
var result = response.results[1];
var child = mentoring.children[active_child];
callIfExists(child, 'handleSubmit', result, options);
callIfExists(child, 'handleReview', result, options);
}
function handleSubmitResults(response){
handleResults(response);
// Update grade information
$('.grade').data(response);
}
function calculate_results(handler_name, callback) {
var data = {};
var child = mentoring.steps[active_child];
if (child && child.name !== undefined) {
data[child.name] = callIfExists(child, 'submit');
data[child.name] = callIfExists(child, handler_name);
}
var handlerUrl = runtime.handlerUrl(element, 'submit');
var handlerUrl = runtime.handlerUrl(element, handler_name);
if (submitXHR) {
submitXHR.abort();
}
submitXHR = $.post(handlerUrl, JSON.stringify(data)).success(handleSubmitResults);
submitXHR = $.post(handlerUrl, JSON.stringify(data)).success(callback);
}
function validateXBlock() {
var is_valid = true;
var data = $('.attempts', element).data();
var steps = mentoring.steps;
function submit() {
calculate_results('submit', handleSubmitResults)
}
// if ((data.max_attempts > 0) && (data.num_attempts >= data.max_attempts)) {
// is_valid = false;
// }
function get_results() {
calculate_results('get_results', handleReviewResults)
}
function validateXBlock(hide_nav) {
var is_valid = true;
var child = mentoring.steps[active_child];
if (child && child.name !== undefined) {
var child_validation = callIfExists(child, 'validate');
......@@ -235,7 +327,7 @@ function MentoringAssessmentView(runtime, element, mentoring) {
submitDOM.removeAttr("disabled");
}
if (isLastChild()) {
if (isLastChild() && ! hide_nav) {
nextDOM.hide();
reviewDOM.show();
}
......
......@@ -4,26 +4,26 @@ function MentoringStandardView(runtime, element, mentoring) {
var callIfExists = mentoring.callIfExists;
function handleSubmitResults(results) {
function handleSubmitResults(response) {
messagesDOM.empty().hide();
$.each(results.submitResults || [], function(index, submitResult) {
var input = submitResult[0];
var result = submitResult[1];
$.each(response.results || [], function(index, result_spec) {
var input = result_spec[0];
var result = result_spec[1];
var child = mentoring.getChildByName(input);
var options = {
max_attempts: results.max_attempts,
num_attempts: results.num_attempts
max_attempts: response.max_attempts,
num_attempts: response.num_attempts
};
callIfExists(child, 'handleSubmit', result, options);
});
$('.attempts', element).data('max_attempts', results.max_attempts);
$('.attempts', element).data('num_attempts', results.num_attempts);
$('.attempts', element).data('max_attempts', response.max_attempts);
$('.attempts', element).data('num_attempts', response.num_attempts);
mentoring.renderAttempts();
// Messages should only be displayed upon hitting 'submit', not on page reload
mentoring.setContent(messagesDOM, results.message);
mentoring.setContent(messagesDOM, response.message);
if (messagesDOM.html().trim()) {
messagesDOM.prepend('<div class="title1">' + gettext('Feedback') + '</div>');
messagesDOM.show();
......@@ -32,23 +32,30 @@ function MentoringStandardView(runtime, element, mentoring) {
submitDOM.attr('disabled', 'disabled');
}
function submit() {
var success = true;
function calculate_results(handler_name) {
var data = {};
var children = mentoring.children;
for (var i = 0; i < children.length; i++) {
var child = children[i];
if (child && child.name !== undefined && typeof(child.submit) !== "undefined") {
data[child.name] = child.submit();
if (child && child.name !== undefined && typeof(child[handler_name]) !== "undefined") {
data[child.name] = child[handler_name]();
}
}
var handlerUrl = runtime.handlerUrl(element, 'submit');
var handlerUrl = runtime.handlerUrl(element, handler_name);
if (submitXHR) {
submitXHR.abort();
}
submitXHR = $.post(handlerUrl, JSON.stringify(data)).success(handleSubmitResults);
}
function get_results() {
calculate_results('get_results');
}
function submit() {
calculate_results('submit')
}
function clearResults() {
messagesDOM.empty().hide();
......@@ -68,6 +75,8 @@ function MentoringStandardView(runtime, element, mentoring) {
submitDOM = $(element).find('.submit .input-main');
submitDOM.bind('click', submit);
submitDOM.show();
// Not used in standard mode.
$(element).find('.review-link').hide();
var options = {
onChange: onChange
......
......@@ -97,23 +97,24 @@ function MCQBlock(runtime, element) {
}
},
handleSubmit: function(result) {
if (this.mode === 'assessment')
return;
handleReview: function(result){
$('.choice input[value="' + result.submission + '"]', element).prop('checked', true);
$('.choice input', element).prop('disabled', true);
},
handleSubmit: function(result) {
mentoring = this.mentoring;
var messageView = MessageView(element, mentoring);
messageView.clearResult();
var choiceInputs = $('.choice input', element);
var choiceInputs = $('.choice-selector input', element);
$.each(choiceInputs, function(index, choiceInput) {
var choiceInputDOM = $(choiceInput);
var choiceDOM = choiceInputDOM.closest('.choice');
var choiceResultDOM = $('.choice-result', choiceDOM);
var choiceTipsDOM = $('.choice-tips', choiceDOM);
var choiceTipsCloseDOM;
if (result.status === "correct" && choiceInputDOM.val() === result.submission) {
choiceDOM.addClass('correct');
......@@ -129,7 +130,6 @@ function MCQBlock(runtime, element) {
messageView.showMessage(choiceTipsDOM);
}
choiceTipsCloseDOM = $('.close', choiceTipsDOM);
choiceResultDOM.off('click').on('click', function() {
if (choiceTipsDOM.html() !== '') {
messageView.showMessage(choiceTipsDOM);
......@@ -178,9 +178,14 @@ function MRQBlock(runtime, element) {
return checkedValues;
},
handleReview: function(result) {
$.each(result.submissions, function (index, value) {
$('input[type="checkbox"][value="' + value + '"]').prop('checked', true)
});
$('input', element).prop('disabled', true);
},
handleSubmit: function(result, options) {
if (this.mode === 'assessment')
return;
mentoring = this.mentoring;
......@@ -193,14 +198,13 @@ function MRQBlock(runtime, element) {
var questionnaireDOM = $('fieldset.questionnaire', element);
var data = questionnaireDOM.data();
var hide_results = (data.hide_results === 'True') ? true : false;
var hide_results = (data.hide_results === 'True');
$.each(result.choices, function(index, choice) {
var choiceInputDOM = $('.choice input[value='+choice.value+']', element);
var choiceDOM = choiceInputDOM.closest('.choice');
var choiceResultDOM = $('.choice-result', choiceDOM);
var choiceTipsDOM = $('.choice-tips', choiceDOM);
var choiceTipsCloseDOM;
/* show hint if checked or max_attempts is disabled */
if (!hide_results &&
......@@ -215,7 +219,6 @@ function MRQBlock(runtime, element) {
mentoring.setContent(choiceTipsDOM, choice.tips);
choiceTipsCloseDOM = $('.close', choiceTipsDOM);
choiceResultDOM.off('click').on('click', function() {
messageView.showMessage(choiceTipsDOM);
});
......
......@@ -26,7 +26,11 @@
data-partially_correct_answer="{{ self.score.4|length }}"
data-max_attempts="{{ self.max_attempts }}"
data-num_attempts="{{ self.num_attempts }}"
data-assessment_message="{{ self.assessment_message }}">
data-extended_feedback="{%if self.extended_feedback %}True{% endif %}"
data-assessment_message="{{ self.assessment_message }}"
data-correct="{{ self.correct_json }}"
data-incorrect="{{ self.incorrect_json }}"
data-partial="{{ self.partial_json }}">
</div>
<div class="assessment-messages"></div>
......@@ -49,4 +53,5 @@
{% endif %}
<div class="messages"></div>
</div>
<div class="review-link"><a href="#">Review final grade</a></div>
</div>
......@@ -22,6 +22,7 @@
</p>
<%= runDetails('correct') %>
</div>
<div class="clear"></div>
<span class="assessment-checkmark icon-2x checkmark-partially-correct icon-ok fa fa-check"></span>
<div class="results-section">
<p>
......@@ -35,6 +36,7 @@
</p>
<%= runDetails('partial') %>
</div>
<div class="clear"></div>
<span class="assessment-checkmark icon-2x checkmark-incorrect icon-exclamation fa fa-exclamation"></span>
<div class="results-section">
<p>
......@@ -48,4 +50,5 @@
</p>
<%= runDetails('incorrect') %>
</div>
<div class="clear"></div>
</script>
......@@ -2,7 +2,7 @@
<% var q, last_question; %>
<ul class="review-list <%= label %>-list">
<% for (var question in questions) {{ q = questions[question]; last_question = question == questions.length - 1; %>
<li><a href="#" class="question-link" data-name="<%= q.id %>"><= gettext("Question {number}", {number: q.number}) =></a></li>
<li><a href="#" class="question-link" data-step="<%= q.number %>"><%= _.template(gettext("Question {number}"), {number: q.number}, {interpolate: /\{(.+?)\}/g}) %></a></li>
<% }} %>
</ul>
</script>
<problem-builder display_name="Mentoring Assessment Example" weight="1" mode="assessment" max_attempts="10">
<problem-builder display_name="Mentoring Assessment Example" weight="1" mode="assessment" max_attempts="2" extended_feedback="true">
<html_demo>
<p>This paragraph is shared between <strong>all</strong> questions.</p>
<p>Please answer the questions below.</p>
</html_demo>
<pb-answer name="goal" question="What is your goal?">
</pb-answer>
<pb-answer name="goal" question="What is your goal?" />
<pb-mcq name="mcq_1_1" question="Do you like this MCQ?" correct_choices="yes">
<pb-mcq name="mcq_1_1" question="Do you like this MCQ?" correct_choices='["yes"]'>
<pb-choice value="yes">Yes</pb-choice>
<pb-choice value="maybenot">Maybe not</pb-choice>
<pb-choice value="understand">I don't understand</pb-choice>
<pb-tip values="yes">Great!</pb-tip>
<pb-tip values="maybenot">Ah, damn.</pb-tip>
<pb-tip values="understand"><div id="test-custom-html">Really?</div></pb-tip>
<pb-tip values='["yes"]'>Great!</pb-tip>
<pb-tip values='["maybenot"]'>Ah, damn.</pb-tip>
<pb-tip values='["understand"]'><div id="test-custom-html">Really?</div></pb-tip>
</pb-mcq>
<pb-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">
<pb-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"]'>
<pb-choice value="notwant">I don't want to rate it</pb-choice>
<pb-tip values="4,5">I love good grades.</pb-tip>
<pb-tip values="1,2,3">Will do better next time...</pb-tip>
<pb-tip values="notwant">Your loss!</pb-tip>
<pb-tip values='["4","5"]'>I love good grades.</pb-tip>
<pb-tip values='["1","2", "3"]'>Will do better next time...</pb-tip>
<pb-tip values='["notwant"]'>Your loss!</pb-tip>
</pb-rating>
<pb-mrq name="mrq_1_1" question="What do you like in this MRQ?" required_choices="gracefulness,elegance,beauty">
<pb-mrq name="mrq_1_1" question="What do you like in this MRQ?" required_choices='["gracefulness","elegance","beauty"]'>
<pb-choice value="elegance">Its elegance</pb-choice>
<pb-choice value="beauty">Its beauty</pb-choice>
<pb-choice value="gracefulness">Its gracefulness</pb-choice>
<pb-choice value="bugs">Its bugs</pb-choice>
<pb-tip values="gracefulness">This MRQ is indeed very graceful</pb-tip>
<pb-tip values="elegance,beauty">This is something everyone has to like about this MRQ</pb-tip>
<pb-tip values="bugs">Nah, there aren't any!</pb-tip>
<pb-tip values='["gracefulness"]'>This MRQ is indeed very graceful</pb-tip>
<pb-tip values='["elegance","beauty"]'>This is something everyone has to like about this MRQ</pb-tip>
<pb-tip values='["bugs"]'>Nah, there aren't any!</pb-tip>
</pb-mrq>
<pb-message type="on-assessment-review">
<html>Assessment additional feedback message text</html>
</pb-message>
......
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