Commit 22e12f6d by Jonathan Piacenti

Implemented extended feedback.

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