Commit 97c5c4d8 by Matjaz Gregoric

Keep items in dropped position in assessment mode.

Items dropped onto wrong zones in standard mode are immediately removed
from the board and sent back to the bank.

In assessment mode, items should stick in the dropped position until the
entire problem is explicitly submitted by clicking a button.

This commit only implements the "stick" part, but does not implement the
submit button and associated handler.

This commit also fixes an existing bug where the template function
would gett reused between blocks.

The template function (which is slightly different for each block,
depending on block's configuration) was getting assigned to the global
DragAndDropBlock function. Each block would overwrite the previous
value, which caused bugs.
parent cd689891
...@@ -190,6 +190,7 @@ class DragAndDropBlock(XBlock, XBlockWithSettingsMixin, ThemableXBlockMixin): ...@@ -190,6 +190,7 @@ class DragAndDropBlock(XBlock, XBlockWithSettingsMixin, ThemableXBlockMixin):
return items return items
return { return {
"mode": self.mode,
"zones": self._get_zones(), "zones": self._get_zones(),
# SDK doesn't supply url_name. # SDK doesn't supply url_name.
"url_name": getattr(self, 'url_name', ''), "url_name": getattr(self, 'url_name', ''),
...@@ -337,12 +338,18 @@ class DragAndDropBlock(XBlock, XBlockWithSettingsMixin, ThemableXBlockMixin): ...@@ -337,12 +338,18 @@ class DragAndDropBlock(XBlock, XBlockWithSettingsMixin, ThemableXBlockMixin):
'is_correct': is_correct, 'is_correct': is_correct,
}) })
return { if self.mode == self.ASSESSMENT_MODE:
'correct': is_correct, # In assessment mode we don't send any feedback on drop.
'finished': self._is_finished(), result = {}
'overall_feedback': overall_feedback, else:
'feedback': feedback result = {
} 'correct': is_correct,
'finished': self._is_finished(),
'overall_feedback': overall_feedback,
'feedback': feedback
}
return result
@XBlock.json_handler @XBlock.json_handler
def reset(self, data, suffix=''): def reset(self, data, suffix=''):
......
function DragNDropTemplates(url_name) { function DragAndDropTemplates(configuration) {
"use strict"; "use strict";
var h = virtualDom.h; var h = virtualDom.h;
// Set up a mock for gettext if it isn't available in the client runtime: // Set up a mock for gettext if it isn't available in the client runtime:
...@@ -107,13 +107,22 @@ function DragNDropTemplates(url_name) { ...@@ -107,13 +107,22 @@ function DragNDropTemplates(url_name) {
var item_content = h('div', { innerHTML: item_content_html, className: "item-content" }); var item_content = h('div', { innerHTML: item_content_html, className: "item-content" });
if (item.is_placed) { if (item.is_placed) {
// Insert information about zone in which this item has been placed // Insert information about zone in which this item has been placed
var item_description_id = url_name + '-item-' + item.value + '-description'; var item_description_id = configuration.url_name + '-item-' + item.value + '-description';
item_content.properties.attributes = { 'aria-describedby': item_description_id }; item_content.properties.attributes = { 'aria-describedby': item_description_id };
var zone_title = (zone.title || "Unknown Zone"); // This "Unknown" text should never be seen, so does not need i18n var zone_title = (zone.title || "Unknown Zone"); // This "Unknown" text should never be seen, so does not need i18n
var description_content;
if (configuration.mode === DragAndDropBlock.ASSESSMENT_MODE) {
// In assessment mode placed items will "stick" even when not in correct zone.
description_content = gettext('Placed in: {zone_title}').replace('{zone_title}', zone_title);
} else {
// In standard mode item is immediately returned back to the bank if dropped on a wrong zone,
// so all placed items are always in the correct zone.
description_content = gettext('Correctly placed in: {zone_title}').replace('{zone_title}', zone_title);
}
var item_description = h( var item_description = h(
'div', 'div',
{ id: item_description_id, className: 'sr' }, { id: item_description_id, className: 'sr' },
gettext('Correctly placed in: ') + zone_title description_content
); );
children.splice(1, 0, item_description); children.splice(1, 0, item_description);
} }
...@@ -283,14 +292,17 @@ function DragNDropTemplates(url_name) { ...@@ -283,14 +292,17 @@ function DragNDropTemplates(url_name) {
); );
}; };
DragAndDropBlock.renderView = mainTemplate; return mainTemplate;
} }
function DragAndDropBlock(runtime, element, configuration) { function DragAndDropBlock(runtime, element, configuration) {
"use strict"; "use strict";
DragNDropTemplates(configuration.url_name); DragAndDropBlock.STANDARD_MODE = 'standard';
DragAndDropBlock.ASSESSMENT_MODE = 'assessment';
var renderView = DragAndDropTemplates(configuration);
// Set up a mock for gettext if it isn't available in the client runtime: // Set up a mock for gettext if it isn't available in the client runtime:
if (!window.gettext) { window.gettext = function gettext_stub(string) { return string; }; } if (!window.gettext) { window.gettext = function gettext_stub(string) { return string; }; }
...@@ -747,17 +759,21 @@ function DragAndDropBlock(runtime, element, configuration) { ...@@ -747,17 +759,21 @@ function DragAndDropBlock(runtime, element, configuration) {
$.post(url, JSON.stringify(data), 'json') $.post(url, JSON.stringify(data), 'json')
.done(function(data){ .done(function(data){
state.last_action_correct = data.correct; state.items[item_id].submitting_location = false;
if (data.correct) { // In standard mode we immediately return item to the bank if dropped on wrong zone.
state.items[item_id].correct = true; // In assessment mode we leave it in the chosen zone until explicit answer submission.
state.items[item_id].submitting_location = false; if (configuration.mode === DragAndDropBlock.STANDARD_MODE) {
} else { state.last_action_correct = data.correct;
delete state.items[item_id]; if (data.correct) {
} state.items[item_id].correct = true;
state.feedback = data.feedback; } else {
if (data.finished) { delete state.items[item_id];
state.finished = true; }
state.overall_feedback = data.overall_feedback; state.feedback = data.feedback;
if (data.finished) {
state.finished = true;
state.overall_feedback = data.overall_feedback;
}
} }
applyState(); applyState();
}) })
...@@ -866,7 +882,7 @@ function DragAndDropBlock(runtime, element, configuration) { ...@@ -866,7 +882,7 @@ function DragAndDropBlock(runtime, element, configuration) {
display_reset_button: Object.keys(state.items).length > 0, display_reset_button: Object.keys(state.items).length > 0,
}; };
return DragAndDropBlock.renderView(context); return renderView(context);
}; };
/** /**
......
...@@ -409,6 +409,10 @@ msgid "ok" ...@@ -409,6 +409,10 @@ msgid "ok"
msgstr "" msgstr ""
#: public/js/drag_and_drop.js #: public/js/drag_and_drop.js
msgid "Placed in: "
msgstr ""
#: public/js/drag_and_drop.js
msgid "Correctly placed in: " msgid "Correctly placed in: "
msgstr "" msgstr ""
......
...@@ -494,6 +494,10 @@ msgid "ok" ...@@ -494,6 +494,10 @@ msgid "ok"
msgstr "ök Ⱡ'σя#" msgstr "ök Ⱡ'σя#"
#: public/js/drag_and_drop.js #: public/js/drag_and_drop.js
msgid "Placed in: "
msgstr "Pläçéd ïn: Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, #"
#: public/js/drag_and_drop.js
msgid "Correctly placed in: " msgid "Correctly placed in: "
msgstr "Çörréçtlý pläçéd ïn: Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, #" msgstr "Çörréçtlý pläçéd ïn: Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, #"
......
{ {
"title": "DnDv2 XBlock with HTML instructions", "title": "DnDv2 XBlock with HTML instructions",
"mode": "standard",
"show_title": false, "show_title": false,
"problem_text": "Solve this <strong>drag-and-drop</strong> problem.", "problem_text": "Solve this <strong>drag-and-drop</strong> problem.",
"show_problem_header": false, "show_problem_header": false,
......
{ {
"title": "Drag and Drop", "title": "Drag and Drop",
"mode": "standard",
"show_title": true, "show_title": true,
"problem_text": "", "problem_text": "",
"show_problem_header": true, "show_problem_header": true,
......
{ {
"title": "DnDv2 XBlock with plain text instructions", "title": "DnDv2 XBlock with plain text instructions",
"mode": "standard",
"show_title": true, "show_title": true,
"problem_text": "Can you solve this drag-and-drop problem?", "problem_text": "Can you solve this drag-and-drop problem?",
"show_problem_header": true, "show_problem_header": true,
......
...@@ -5,6 +5,8 @@ import unittest ...@@ -5,6 +5,8 @@ import unittest
from xblockutils.resources import ResourceLoader from xblockutils.resources import ResourceLoader
from drag_and_drop_v2.drag_and_drop_v2 import DragAndDropBlock
from ..utils import make_block, TestCaseMixin from ..utils import make_block, TestCaseMixin
...@@ -90,6 +92,14 @@ class BaseDragAndDropAjaxFixture(TestCaseMixin): ...@@ -90,6 +92,14 @@ class BaseDragAndDropAjaxFixture(TestCaseMixin):
"feedback": self.FEEDBACK[item_id]["correct"] "feedback": self.FEEDBACK[item_id]["correct"]
}) })
def test_do_attempt_in_assessment_mode(self):
self.block.mode = DragAndDropBlock.ASSESSMENT_MODE
item_id, zone_id = 0, self.ZONE_1
data = {"val": item_id, "zone": zone_id, "x_percent": "33%", "y_percent": "11%"}
res = self.call_handler('do_attempt', data)
# In assessment mode, the do_attempt doesn't return any data.
self.assertEqual(res, {})
def test_grading(self): def test_grading(self):
published_grades = [] published_grades = []
......
...@@ -30,6 +30,7 @@ class BasicTests(TestCaseMixin, unittest.TestCase): ...@@ -30,6 +30,7 @@ class BasicTests(TestCaseMixin, unittest.TestCase):
zones = config.pop("zones") zones = config.pop("zones")
items = config.pop("items") items = config.pop("items")
self.assertEqual(config, { self.assertEqual(config, {
"mode": DragAndDropBlock.STANDARD_MODE,
"display_zone_borders": False, "display_zone_borders": False,
"display_zone_labels": False, "display_zone_labels": False,
"title": "Drag and Drop", "title": "Drag and Drop",
......
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