Commit ae4ef34e by Matjaz Gregoric

Ensure old problems work on new version.

Recent versions of the drag and drop v2 xblock require the 'correct'
field to be present in items stored in user state, but the 'correct'
field did not exist before assessment mode has been implemented and so
problems created and completed before the introduction of assessment
mode lack the 'correct' field.

This patch adds the 'correct' field to item state at load time.
In standard mode, only items that are placed in correct zone are stored
in item state, so we can assume that any item stored in the state that
does not have the 'correct' field is 'correct'.
parent 6b6574ee
......@@ -638,25 +638,11 @@ class DragAndDropBlock(XBlock, XBlockWithSettingsMixin, ThemableXBlockMixin):
def _get_user_state(self):
""" Get all user-specific data, and any applicable feedback """
item_state = self._get_item_state()
for item_id, item in item_state.iteritems():
# If information about zone is missing
# (because problem was completed before a11y enhancements were implemented),
# deduce zone in which item is placed from definition:
if item.get('zone') is None:
valid_zones = self._get_item_zones(int(item_id))
if valid_zones:
# If we get to this point, then the item was placed prior to support for
# multiple correct zones being added. As a result, it can only be correct
# on a single zone, and so we can trust that the item was placed on the
# zone with index 0.
item['zone'] = valid_zones[0]
else:
item['zone'] = 'unknown'
# In assessment mode, if item is placed correctly and than the page is refreshed, "correct"
# will spill to the frontend, making item "disabled", thus allowing students to obtain answer by trial
# and error + refreshing the page. In order to avoid that, we remove "correct" from an item here
if self.mode == self.ASSESSMENT_MODE:
for item in item_state.values():
del item["correct"]
overall_feedback_msgs, __ = self._get_feedback()
......@@ -682,11 +668,32 @@ class DragAndDropBlock(XBlock, XBlockWithSettingsMixin, ThemableXBlockMixin):
# handler and manipulated there to hide correctness of items placed
state = {}
for item_id, item in self.item_state.iteritems():
if isinstance(item, dict):
state[item_id] = item.copy() # items are manipulated in _get_user_state, so we protect actual data
for item_id, raw_item in self.item_state.iteritems():
if isinstance(raw_item, dict):
# Items are manipulated in _get_user_state, so we protect actual data.
item = copy.deepcopy(raw_item)
else:
state[item_id] = {'top': item[0], 'left': item[1]}
item = {'top': raw_item[0], 'left': raw_item[1]}
# If information about zone is missing
# (because problem was completed before a11y enhancements were implemented),
# deduce zone in which item is placed from definition:
if item.get('zone') is None:
valid_zones = self._get_item_zones(int(item_id))
if valid_zones:
# If we get to this point, then the item was placed prior to support for
# multiple correct zones being added. As a result, it can only be correct
# on a single zone, and so we can trust that the item was placed on the
# zone with index 0.
item['zone'] = valid_zones[0]
else:
item['zone'] = 'unknown'
# If correctness information is missing
# (because problem was completed before assessment mode was implemented),
# assume the item is in correct zone (in standard mode, only items placed
# into correct zone are stored in item state).
if item.get('correct') is None:
item['correct'] = True
state[item_id] = item
return state
......
......@@ -109,6 +109,34 @@ class BasicTests(TestCaseMixin, unittest.TestCase):
self.assertTrue(self.block.completed)
assert_user_state_empty()
def test_legacy_state_support(self):
"""
The form of items stored in user item_state has changed several times.
This test makes sure that legacy forms are properly converted to compatible format.
"""
self.assertEqual(self.block.item_state, {})
self.assertEqual(self.call_handler('get_user_state')['items'], {})
self.block.item_state = {
# Legacy tuple (top, left) representation.
'0': [60, 20],
# Legacy dict with absolute values and no correctness or zone info.
'1': {'top': 45, 'left': 99},
# Legacy dict with no correctness info.
'2': {'x_percent': '99%', 'y_percent': '95%', 'zone': BOTTOM_ZONE_ID},
# Current dict form.
'3': {'x_percent': '67%', 'y_percent': '80%', 'zone': BOTTOM_ZONE_ID, 'correct': False},
}
self.block.save()
self.assertEqual(self.call_handler('get_user_state')['items'], {
# Legacy top/left values are converted to x/y percentage on the client.
'0': {'top': 60, 'left': 20, 'correct': True, 'zone': TOP_ZONE_ID},
'1': {'top': 45, 'left': 99, 'correct': True, 'zone': MIDDLE_ZONE_ID},
'2': {'x_percent': '99%', 'y_percent': '95%', 'correct': True, 'zone': BOTTOM_ZONE_ID},
'3': {'x_percent': '67%', 'y_percent': '80%', 'correct': False, "zone": BOTTOM_ZONE_ID},
})
def test_studio_submit(self):
body = {
'display_name': "Test Drag & 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