Commit 9cf2ee45 by Tim Krones

Implement submission logic.

parent af8f576f
...@@ -4,24 +4,50 @@ function MentoringWithStepsBlock(runtime, element) { ...@@ -4,24 +4,50 @@ function MentoringWithStepsBlock(runtime, element) {
function(c) { return c.element.className.indexOf('pb-mentoring-step') > -1; } function(c) { return c.element.className.indexOf('pb-mentoring-step') > -1; }
); );
var active_child = -1; var active_child = -1;
var submitDOM, nextDOM; var checkmark, submitDOM, nextDOM;
function isLastChild() { function isLastChild() {
return (active_child === steps.length-1); return (active_child === steps.length-1);
} }
function handleResults(response) {
if (response.completed === 'correct') {
checkmark.addClass('checkmark-correct icon-ok fa-check');
} else if (response.completed === 'partial') {
checkmark.addClass('checkmark-partially-correct icon-ok fa-check');
} else {
checkmark.addClass('checkmark-incorrect icon-exclamation fa-exclamation');
}
submitDOM.attr('disabled', 'disabled');
nextDOM.removeAttr("disabled");
if (nextDOM.is(':visible')) { nextDOM.focus(); }
}
function submit() {
// We do not handle submissions at this level, so just forward to "submit" method of current step
var child = steps[active_child];
child['submit'](handleResults);
}
function hideAllSteps() { function hideAllSteps() {
for (var i=0; i < steps.length; i++) { for (var i=0; i < steps.length; i++) {
$(steps[i].element).hide(); $(steps[i].element).hide();
} }
} }
function displayNextChild() { function cleanAll() {
checkmark.removeClass('checkmark-correct icon-ok fa-check');
checkmark.removeClass('checkmark-partially-correct icon-ok fa-check');
checkmark.removeClass('checkmark-incorrect icon-exclamation fa-exclamation');
hideAllSteps(); hideAllSteps();
}
function displayNextChild() {
cleanAll();
findNextChild(); findNextChild();
if (isLastChild()) {
nextDOM.attr('disabled', 'disabled'); nextDOM.attr('disabled', 'disabled');
}
validateXBlock(); validateXBlock();
} }
...@@ -33,8 +59,13 @@ function MentoringWithStepsBlock(runtime, element) { ...@@ -33,8 +59,13 @@ function MentoringWithStepsBlock(runtime, element) {
} }
function onChange() { function onChange() {
// We do not allow users to modify answers belonging to a step after submitting them:
// Once an answer has been submitted (next button is enabled),
// start ignoring changes to the answer.
if (nextDOM.attr('disabled')) {
validateXBlock(); validateXBlock();
} }
}
function validateXBlock() { function validateXBlock() {
var is_valid = true; var is_valid = true;
...@@ -47,6 +78,9 @@ function MentoringWithStepsBlock(runtime, element) { ...@@ -47,6 +78,9 @@ function MentoringWithStepsBlock(runtime, element) {
} else { } else {
submitDOM.removeAttr('disabled'); submitDOM.removeAttr('disabled');
} }
if (isLastChild()) {
nextDOM.hide();
}
} }
function initSteps(options) { function initSteps(options) {
...@@ -57,12 +91,14 @@ function MentoringWithStepsBlock(runtime, element) { ...@@ -57,12 +91,14 @@ function MentoringWithStepsBlock(runtime, element) {
} }
function initXBlockView() { function initXBlockView() {
checkmark = $('.assessment-checkmark', element);
submitDOM = $(element).find('.submit .input-main'); submitDOM = $(element).find('.submit .input-main');
submitDOM.bind('click', submit);
submitDOM.show(); submitDOM.show();
nextDOM = $(element).find('.submit .input-next'); nextDOM = $(element).find('.submit .input-next');
nextDOM.bind('click', displayNextChild); nextDOM.bind('click', displayNextChild);
nextDOM.removeAttr('disabled');
nextDOM.show(); nextDOM.show();
var options = { var options = {
......
function MentoringStepBlock(runtime, element) { function MentoringStepBlock(runtime, element) {
var children = runtime.children(element); var children = runtime.children(element);
var submitXHR;
function callIfExists(obj, fn) { function callIfExists(obj, fn) {
if (typeof obj !== 'undefined' && typeof obj[fn] == 'function') { if (typeof obj !== 'undefined' && typeof obj[fn] == 'function') {
...@@ -31,6 +32,25 @@ function MentoringStepBlock(runtime, element) { ...@@ -31,6 +32,25 @@ function MentoringStepBlock(runtime, element) {
} }
} }
return is_valid; return is_valid;
},
submit: function(result_handler) {
var handler_name = 'submit';
var data = {};
for (var i = 0; i < children.length; i++) {
var child = children[i];
if (child && child.name !== undefined && typeof(child[handler_name]) !== "undefined") {
data[child.name.toString()] = child[handler_name]();
}
}
var handlerUrl = runtime.handlerUrl(element, handler_name);
if (submitXHR) {
submitXHR.abort();
}
submitXHR = $.post(handlerUrl, JSON.stringify(data))
.success(function(response) {
result_handler(response);
});
} }
}; };
......
...@@ -18,12 +18,13 @@ ...@@ -18,12 +18,13 @@
# "AGPLv3". If not, see <http://www.gnu.org/licenses/>. # "AGPLv3". If not, see <http://www.gnu.org/licenses/>.
# #
import logging
from lazy.lazy import lazy from lazy.lazy import lazy
from xblock.core import XBlock from xblock.core import XBlock
from xblock.fields import String, Boolean, Scope from xblock.fields import String, List, Scope
from xblock.fragment import Fragment from xblock.fragment import Fragment
from xblockutils.helpers import child_isinstance
from xblockutils.resources import ResourceLoader from xblockutils.resources import ResourceLoader
from xblockutils.studio_editable import ( from xblockutils.studio_editable import (
NestedXBlockSpec, StudioEditableXBlockMixin, StudioContainerWithNestedXBlocksMixin, XBlockWithPreviewMixin NestedXBlockSpec, StudioEditableXBlockMixin, StudioContainerWithNestedXBlocksMixin, XBlockWithPreviewMixin
...@@ -31,11 +32,12 @@ from xblockutils.studio_editable import ( ...@@ -31,11 +32,12 @@ from xblockutils.studio_editable import (
from problem_builder.answer import AnswerBlock, AnswerRecapBlock from problem_builder.answer import AnswerBlock, AnswerRecapBlock
from problem_builder.mcq import MCQBlock, RatingBlock from problem_builder.mcq import MCQBlock, RatingBlock
from problem_builder.mixins import EnumerableChildMixin from problem_builder.mixins import EnumerableChildMixin, StepParentMixin
from problem_builder.mrq import MRQBlock from problem_builder.mrq import MRQBlock
from problem_builder.table import MentoringTableBlock from problem_builder.table import MentoringTableBlock
log = logging.getLogger(__name__)
loader = ResourceLoader(__name__) loader = ResourceLoader(__name__)
...@@ -64,7 +66,7 @@ class HtmlBlockShim(object): ...@@ -64,7 +66,7 @@ class HtmlBlockShim(object):
@XBlock.needs('i18n') @XBlock.needs('i18n')
class MentoringStepBlock( class MentoringStepBlock(
StudioEditableXBlockMixin, StudioContainerWithNestedXBlocksMixin, XBlockWithPreviewMixin, StudioEditableXBlockMixin, StudioContainerWithNestedXBlocksMixin, XBlockWithPreviewMixin,
EnumerableChildMixin, XBlock EnumerableChildMixin, StepParentMixin, XBlock
): ):
""" """
An XBlock for a step. An XBlock for a step.
...@@ -80,6 +82,11 @@ class MentoringStepBlock( ...@@ -80,6 +82,11 @@ class MentoringStepBlock(
default="", default="",
scope=Scope.content scope=Scope.content
) )
student_results = List(
# Store results of student choices.
default=[],
scope=Scope.user_state
)
editable_fields = ('display_name', 'show_title',) editable_fields = ('display_name', 'show_title',)
...@@ -104,13 +111,38 @@ class MentoringStepBlock( ...@@ -104,13 +111,38 @@ class MentoringStepBlock(
AnswerRecapBlock, MentoringTableBlock, AnswerRecapBlock, MentoringTableBlock,
] ]
@property @XBlock.json_handler
def steps(self): def submit(self, submissions, suffix=''):
""" Get the usage_ids of all of this XBlock's children that are "Questions" """ log.info(u'Received submissions: {}'.format(submissions))
from mixins import QuestionMixin
return [ # Submit child blocks (questions) and gather results
_normalize_id(child_id) for child_id in self.children if child_isinstance(self, child_id, QuestionMixin) submit_results = []
] for child in self.get_steps():
if child.name and child.name in submissions:
submission = submissions[child.name]
child_result = child.submit(submission)
submit_results.append([child.name, child_result])
child.save() # FIXME: Is this necessary? Child blocks also save themselves ...
# Update results stored for this step
while self.student_results:
self.student_results.pop()
for result in submit_results:
self.student_results.append(result)
# Compute "answer status" for this step
if all(result[1]['status'] == 'correct' for result in submit_results):
completed = 'correct'
elif all(result[1]['status'] == 'incorrect' for result in submit_results):
completed = 'incorrect'
else:
completed = 'partial'
return {
'message': 'Success!',
'completed': completed,
'results': submit_results,
}
def author_edit_view(self, context): def author_edit_view(self, context):
""" """
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
<div class="assessment-question-block"> <div class="assessment-question-block">
{{child_content|safe}} {{child_content|safe}}
<div class="submit"> <div class="submit">
<span class="assessment-checkmark fa icon-2x"></span>
<input type="button" class="input-main" value="Submit" disabled="disabled" /> <input type="button" class="input-main" value="Submit" disabled="disabled" />
<input type="button" class="input-next" value="Next Step" disabled="disabled" /> <input type="button" class="input-next" value="Next Step" disabled="disabled" />
</div> </div>
......
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