Commit 4085f3ee by Xavier Antoviaque

Merge pull request #11 from dragonfi/reset-button

Add button for student to reset the problem
parents 005bd8a5 397266ec
...@@ -10,7 +10,7 @@ import copy ...@@ -10,7 +10,7 @@ import copy
import urllib import urllib
from xblock.core import XBlock from xblock.core import XBlock
from xblock.fields import Scope, String, Dict, Float from xblock.fields import Scope, String, Dict, Float, Boolean
from xblock.fragment import Fragment from xblock.fragment import Fragment
from .utils import render_template, load_resource from .utils import render_template, load_resource
...@@ -62,6 +62,12 @@ class DragAndDropBlock(XBlock): ...@@ -62,6 +62,12 @@ class DragAndDropBlock(XBlock):
default={} default={}
) )
completed = Boolean(
help="The student has completed the problem at least once",
scope=Scope.user_state,
default=False
)
has_score = True has_score = True
def student_view(self, context): def student_view(self, context):
...@@ -147,13 +153,12 @@ class DragAndDropBlock(XBlock): ...@@ -147,13 +153,12 @@ class DragAndDropBlock(XBlock):
del item['feedback'] del item['feedback']
del item['zone'] del item['zone']
tot_items = sum(1 for i in self.data['items'] if i['zone'] != 'none') if not self._is_finished():
if len(self.item_state) != tot_items:
del data['feedback']['finish'] del data['feedback']['finish']
data['state'] = { data['state'] = {
'items': self.item_state, 'items': self.item_state,
'finished': len(self.item_state) == tot_items 'finished': self._is_finished()
} }
return webob.response.Response(body=json.dumps(data)) return webob.response.Response(body=json.dumps(data))
...@@ -171,18 +176,21 @@ class DragAndDropBlock(XBlock): ...@@ -171,18 +176,21 @@ class DragAndDropBlock(XBlock):
is_correct = True is_correct = True
if len(self.item_state) == tot_items: if self._is_finished():
final_feedback = self.data['feedback']['finish'] final_feedback = self.data['feedback']['finish']
try: # only publish the grade once
self.runtime.publish(self, 'grade', { if not self.completed:
'value': len(self.item_state) / float(tot_items) * self.weight, self.completed = True
'max_value': self.weight, try:
}) self.runtime.publish(self, 'grade', {
except NotImplementedError: 'value': len(self.item_state) / float(tot_items) * self.weight,
# Note, this publish method is unimplemented in Studio runtimes, 'max_value': self.weight,
# so we have to figure that we're running in Studio for now })
pass except NotImplementedError:
# Note, this publish method is unimplemented in Studio runtimes,
# so we have to figure that we're running in Studio for now
pass
self.runtime.publish(self, 'xblock.drag-and-drop-v2.item.dropped', { self.runtime.publish(self, 'xblock.drag-and-drop-v2.item.dropped', {
'item_id': item['id'], 'item_id': item['id'],
...@@ -192,12 +200,22 @@ class DragAndDropBlock(XBlock): ...@@ -192,12 +200,22 @@ class DragAndDropBlock(XBlock):
return { return {
'correct': is_correct, 'correct': is_correct,
'finished': len(self.item_state) == tot_items, 'finished': self._is_finished(),
'final_feedback': final_feedback, 'final_feedback': final_feedback,
'feedback': item['feedback']['correct'] if is_correct else item['feedback']['incorrect'] 'feedback': item['feedback']['correct'] if is_correct else item['feedback']['incorrect']
} }
@XBlock.json_handler @XBlock.json_handler
def reset(self, data, suffix=''):
self.item_state = {}
return {'result':'success'}
def _is_finished(self):
"""All items are at their correct place"""
tot_items = sum(1 for i in self.data['items'] if i['zone'] != 'none')
return len(self.item_state) == tot_items
@XBlock.json_handler
def publish_event(self, data, suffix=''): def publish_event(self, data, suffix=''):
try: try:
event_type = data.pop('event_type') event_type = data.pop('event_type')
......
...@@ -38,6 +38,7 @@ ...@@ -38,6 +38,7 @@
font-weight: bold; font-weight: bold;
font-style: normal; font-style: normal;
margin: 10px 0; margin: 10px 0;
margin-top: 20px;
} }
...@@ -144,7 +145,6 @@ ...@@ -144,7 +145,6 @@
.xblock--drag-and-drop .feedback { .xblock--drag-and-drop .feedback {
margin-top: 20px; margin-top: 20px;
border-top: solid 1px rgb(189, 189, 189); border-top: solid 1px rgb(189, 189, 189);
padding-top: 20px;
} }
.xblock--drag-and-drop .popup { .xblock--drag-and-drop .popup {
...@@ -179,3 +179,10 @@ ...@@ -179,3 +179,10 @@
font-family: "fontawesome"; font-family: "fontawesome";
font-size: 18pt; font-size: 18pt;
} }
.xblock--drag-and-drop .reset-button {
cursor: pointer;
float: right;
color: #3384CA;
margin-top: 3px;
}
...@@ -15,6 +15,7 @@ function DragAndDropBlock(runtime, element) { ...@@ -15,6 +15,7 @@ function DragAndDropBlock(runtime, element) {
$target: $('.xblock--drag-and-drop .target-img', element), $target: $('.xblock--drag-and-drop .target-img', element),
$feedback: $('.xblock--drag-and-drop .feedback .message', element), $feedback: $('.xblock--drag-and-drop .feedback .message', element),
$popup: $('.xblock--drag-and-drop .popup', element), $popup: $('.xblock--drag-and-drop .popup', element),
$reset_button: $('.xblock--drag-and-drop .reset-button', element),
// Cannot set until items added to DOM // Cannot set until items added to DOM
$items: {}, // $('.xblock--drag-and-drop .items .option'), $items: {}, // $('.xblock--drag-and-drop .items .option'),
...@@ -82,6 +83,15 @@ function DragAndDropBlock(runtime, element) { ...@@ -82,6 +83,15 @@ function DragAndDropBlock(runtime, element) {
if (final_feedback) _fn.feedback.set(final_feedback); if (final_feedback) _fn.feedback.set(final_feedback);
}, },
reset: function() {
_fn.$items.draggable('enable');
_fn.$items.each(function(index, element) {
_fn.clickHandlers.drag.reset($(element));
});
_fn.$popup.hide();
_fn.feedback.set(_fn.data.feedback.start);
},
clickHandlers: { clickHandlers: {
init: function($drag, $dropzone) { init: function($drag, $dropzone) {
var clk = _fn.clickHandlers; var clk = _fn.clickHandlers;
...@@ -92,14 +102,28 @@ function DragAndDropBlock(runtime, element) { ...@@ -92,14 +102,28 @@ function DragAndDropBlock(runtime, element) {
$dropzone.on('drop', clk.drop.success); $dropzone.on('drop', clk.drop.success);
$dropzone.on('dropover', clk.drop.hover); $dropzone.on('dropover', clk.drop.hover);
$(".close", _fn.$popup).on('click', function() { $(".close", _fn.$popup).on('click', clk.popup.close);
_fn.$reset_button.on('click', clk.problem.reset);
},
problem: {
reset: function(event, ui) {
$.ajax({
type: "POST",
url: runtime.handlerUrl(element, "reset"),
data: "{}",
success: _fn.reset
});
}
},
popup: {
close: function(event, ui) {
_fn.$popup.hide(); _fn.$popup.hide();
publish_event({ publish_event({
event_type: 'xblock.drag-and-drop-v2.feedback.closed', event_type: 'xblock.drag-and-drop-v2.feedback.closed',
content: _fn.$popup.find(".popup-content").text(), content: _fn.$popup.find(".popup-content").text(),
manually: true manually: true
}); });
}); }
}, },
drag: { drag: {
start: function(event, ui) { start: function(event, ui) {
......
...@@ -24,7 +24,9 @@ ...@@ -24,7 +24,9 @@
<div class="clear"></div> <div class="clear"></div>
</section> </section>
<section class="feedback"> <section class="feedback">
<div class="reset-button">Reset exercise</div>
<div class="title1">Feedback</div> <div class="title1">Feedback</div>
<p class="message"></p> <p class="message"></p>
</section> </section>
......
...@@ -11,8 +11,8 @@ from workbench.runtime import WorkbenchRuntime ...@@ -11,8 +11,8 @@ from workbench.runtime import WorkbenchRuntime
from xblock.runtime import KvsFieldData, DictKeyValueStore from xblock.runtime import KvsFieldData, DictKeyValueStore
from nose.tools import ( from nose.tools import (
assert_equals, assert_true, assert_in, assert_equals, assert_true, assert_false,
assert_regexp_matches assert_in, assert_regexp_matches
) )
import drag_and_drop_v2 import drag_and_drop_v2
...@@ -155,3 +155,21 @@ def test_ajax(): ...@@ -155,3 +155,21 @@ def test_ajax():
get_data = json.loads(block.handle('get_data', Mock()).body) get_data = json.loads(block.handle('get_data', Mock()).body)
assert_equals(expected, get_data) assert_equals(expected, get_data)
def test_ajax_solve_and_reset():
block = make_block()
assert_false(block.completed)
assert_equals(block.item_state, {})
data = json.dumps({"val":0,"zone":"Zone A","top":"11px","left":"111px"})
block.handle('do_attempt', make_request(data))
data = json.dumps({"val":1,"zone":"Zone B","top":"22px","left":"222px"})
block.handle('do_attempt', make_request(data))
assert_true(block.completed)
assert_equals(block.item_state, {0:("11px", "111px"), 1:("22px", "222px")})
block.handle('reset', make_request("{}"))
assert_true(block.completed)
assert_equals(block.item_state, {})
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