drag_and_drop_input.rst 10.8 KB
Newer Older
1 2 3 4 5 6 7 8 9
**********************************************
Xml format of drag and drop input [inputtypes]
**********************************************

.. module:: drag_and_drop_input

Format description
==================

10
The main tag of Drag and Drop (DnD) input is::
11

12
    <drag_and_drop_input> ... </drag_and_drop_input>
13

14 15
``drag_and_drop_input`` can include any number of the following 2 tags:
``draggable`` and ``target``.
16

17 18
drag_and_drop_input tag
-----------------------
19

20 21
The main container for a single instance of DnD. The following attributes can
be specified for this tag::
22

23 24 25 26 27 28 29 30 31
    img - Relative path to an image that will be the base image. All draggables
          can be dragged onto it.
    target_outline - Specify whether an outline (gray dashed line) should be
          drawn around targets (if they are specified). It can be either
          'true' or 'false'. If not specified, the default value is
          'false'.
    one_per_target - Specify whether to allow more than one draggable to be
          placed onto a single target. It can be either 'true' or 'false'. If
          not specified, the default value is 'true'.
32 33 34
    no_labels - default is false, in default behaviour if label is not set, label
          is obtained from id. If no_labels is true, labels are not automatically
          populated from id, and one can not set labels and obtain only icons.
35

36 37
draggable tag
-------------
38

39
Draggable tag specifies a single draggable object which has the following
40 41
attributes::

42 43 44
    id - Unique identifier of the draggable object.
    label - Human readable label that will be shown to the user.
    icon - Relative path to an image that will be shown to the user.
Alexander Kryklia committed
45 46
    can_reuse - true or false, default is false. If true, same draggable can be
    used multiple times.
47

48 49 50 51
A draggable is what the user must drag out of the slider and place onto the
base image. After a drag operation, if the center of the draggable ends up
outside the rectangular dimensions of the image, it will be returned back
to the slider.
52

53 54 55 56
In order for the grader to work, it is essential that a unique ID
is provided. Otherwise, there will be no way to tell which draggable is at what
coordinate, or over what target. Label and icon attributes are optional. If
they are provided they will be used, otherwise, you can have an empty
57 58
draggable. The path is relative to 'course_folder' folder, for example,
/static/images/img1.png.
59

60 61
target tag
----------
62

63 64
Target tag specifies a single target object which has the following required
attributes::
65

66 67 68 69 70 71 72
    id - Unique identifier of the target object.
    x - X-coordinate on the base image where the top left corner of the target
        will be positioned.
    y - Y-coordinate on the base image where the top left corner of the target
        will be positioned.
    w - Width of the target.
    h - Height of the target.
73

74 75 76 77
A target specifies a place on the base image where a draggable can be
positioned. By design, if the center of a draggable lies within the target
(i.e. in the rectangle defined by [[x, y], [x + w, y + h]], then it is within
the target. Otherwise, it is outside.
78

79 80 81
If at lest one target is provided, the behavior of the client side logic
changes. If a draggable is not dragged on to a target, it is returned back to
the slider.
82

83 84
If no targets are provided, then a draggable can be dragged and placed anywhere
on the base image.
85

Alexander Kryklia committed
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
correct answer format
---------------------

There are two correct answer formats: short and long
If short from correct answer is mapping of 'draggable_id' to 'target_id'::

    correct_answer = {'grass':     [[300, 200], 200], 'ant': [[500, 0], 200]}
    correct_answer = {'name4': 't1', '7': 't2'}

In long form correct answer is list of dicts. Every dict has 3 keys:
draggables, targets and rule. For example::

    correct_answer = [
    {
    'draggables':   ['7', '8'],
    'targets':  ['t5_c', 't6_c'],
    'rule': 'anyof'
    },
    {
    'draggables': ['1', '2'],
    'targets': ['t2_h', 't3_h', 't4_h', 't7_h', 't8_h', 't10_h'],
    'rule': 'anyof'
    }]

Draggables is list of draggables id. Target is list of targets id, draggables
must be dragged to with considering rule. Rule is string.

Alexander Kryklia committed
113 114
Draggables in dicts inside correct_answer list must not intersect!!!

Alexander Kryklia committed
115 116 117 118 119 120 121 122 123 124 125 126 127 128
Wrong (for draggable id 7)::

    correct_answer = [
    {
    'draggables':   ['7', '8'],
    'targets':  ['t5_c', 't6_c'],
    'rule': 'anyof'
    },
    {
    'draggables': ['7', '2'],
    'targets': ['t2_h', 't3_h', 't4_h', 't7_h', 't8_h', 't10_h'],
    'rule': 'anyof'
    }]

Alexander Kryklia committed
129
Rules are: exact, anyof, unordered_equal, anyof+number, unordered_equal+number
Alexander Kryklia committed
130

Alexander Kryklia committed
131 132 133 134

.. such long lines are needed for sphinx to display lists correctly

- Exact rule means that targets for draggable id's in user_answer are the same that targets from correct answer. For example, for draggables 7 and 8 user must drag 7 to target1 and 8 to target2 if correct_answer is::
Alexander Kryklia committed
135 136 137 138 139 140 141 142 143

    correct_answer = [
    {
    'draggables':   ['7', '8'],
    'targets':  ['tartget1', 'target2'],
    'rule': 'exact'
    }]


Alexander Kryklia committed
144
- unordered_equal rule allows draggables be dragged to targets unordered. If one want to allow for student to drag 7 to target1 or target2 and 8 to target2 or target 1 and 7 and 8 must be in different targets, then correct answer must be::
Alexander Kryklia committed
145 146 147 148 149

    correct_answer = [
    {
    'draggables':   ['7', '8'],
    'targets':  ['tartget1', 'target2'],
Alexander Kryklia committed
150
    'rule': 'unordered_equal'
Alexander Kryklia committed
151 152
    }]

Alexander Kryklia committed
153 154

- Anyof rule allows draggables to be dragged to any of targets. If one want to allow for student to drag 7 and 8 to target1 or target2, which means that if 7 is on target1 and 8 is on target1 or 7 on target2 and 8 on target2 or 7 on target1 and 8 on target2. Any of theese are correct which anyof rule::
Alexander Kryklia committed
155 156 157 158 159 160 161 162 163

    correct_answer = [
    {
    'draggables':   ['7', '8'],
    'targets':  ['tartget1', 'target2'],
    'rule': 'anyof'
    }]


Alexander Kryklia committed
164
- If you have can_reuse true, then you, for example, have draggables a,b,c and 10 targets. These will allow you to drag 4 'a' draggables to ['target1',  'target4', 'target7', 'target10'] , you do not need to write 'a' four times. Also this will allow you to drag 'b' draggable to target2 or target5 for target5 and target2 etc..::
Alexander Kryklia committed
165 166 167 168 169

    correct_answer = [
        {
            'draggables': ['a'],
            'targets': ['target1',  'target4', 'target7', 'target10'],
Alexander Kryklia committed
170
            'rule': 'unordered_equal'
Alexander Kryklia committed
171 172 173 174 175 176 177 178 179
        },
        {
            'draggables': ['b'],
            'targets': ['target2', 'target5', 'target8'],
            'rule': 'anyof'
        },
        {
            'draggables': ['c'],
            'targets': ['target3', 'target6', 'target9'],
Alexander Kryklia committed
180
            'rule': 'unordered_equal'
Alexander Kryklia committed
181 182
        }]

Alexander Kryklia committed
183
- And sometimes you want to allow drag only two 'b' draggables, in these case you sould use 'anyof+number' of 'unordered_equal+number' rule::
Alexander Kryklia committed
184 185 186 187 188

    correct_answer = [
        {
            'draggables': ['a', 'a', 'a'],
            'targets': ['target1',  'target4', 'target7'],
Alexander Kryklia committed
189
            'rule': 'unordered_equal+numbers'
Alexander Kryklia committed
190 191 192 193 194 195 196 197 198
        },
        {
            'draggables': ['b', 'b'],
            'targets': ['target2', 'target5', 'target8'],
            'rule': 'anyof+numbers'
        },
        {
            'draggables': ['c'],
            'targets': ['target3', 'target6', 'target9'],
Alexander Kryklia committed
199
            'rule': 'unordered_equal'
Alexander Kryklia committed
200 201
        }]

Alexander Kryklia committed
202 203
In case if we have no multiple draggables per targets (one_per_target="true"),
for same number of draggables, anyof is equal to unordered_equal
204

Alexander Kryklia committed
205 206
If we have can_reuse=true, than one must use only long form of correct answer.

Alexander Kryklia committed
207 208 209 210

Grading logic
-------------

Alexander Kryklia committed
211
1. User answer (that comes from browser) and correct answer (from xml) are parsed to the same format::
Alexander Kryklia committed
212 213 214

    group_id: group_draggables, group_targets, group_rule

Alexander Kryklia committed
215

Alexander Kryklia committed
216 217 218 219
Group_id is ordinal number, for every dict in correct answer incremental
group_id is assigned: 0, 1, 2, ...

Draggables from user answer are added to same group_id where identical draggables
Alexander Kryklia committed
220
from correct answer are, for example::
Alexander Kryklia committed
221 222

    If correct_draggables[group_0] = [t1, t2] then
Alexander Kryklia committed
223 224
    user_draggables[group_0] are all draggables t1 and t2 from user answer:
    [t1] or [t1, t2] or [t1, t2, t2] etc..
Alexander Kryklia committed
225

Alexander Kryklia committed
226 227
2. For every group from user answer, for that group draggables, if 'number' is in  group rule, set() is applied,
if 'number' is not in rule, set is not applied::
Alexander Kryklia committed
228

Alexander Kryklia committed
229
    set() : [t1, t2, t3, t3] -> [t1, t2, ,t3]
Alexander Kryklia committed
230

Alexander Kryklia committed
231 232 233 234 235 236 237 238 239 240
For every group, at this step, draggables lists are equal.


3. For every group, lists of targets are compared using rule for that group.


Set and '+number' cases
.......................

Set() and '+number' are needed only for case of reusable draggables,
Alexander Kryklia committed
241 242
for other cases there are no equal draggables in list, so set() does nothing.

Alexander Kryklia committed
243 244 245 246 247 248 249 250 251 252 253
.. such long lines needed for sphinx to display nicely

* Usage of set() operation allows easily create rule for case of "any number of same draggable can be dragged to some targets"::

        {
                'draggables': ['draggable_1'],
                'targets': ['target3', 'target6', 'target9'],
                'rule': 'anyof'
        }


Alexander Kryklia committed
254 255


Alexander Kryklia committed
256
* 'number' rule is used for the case of reusable draggables, when one want to fix number of draggable to drag. In this example only two instances of draggables_1 are allowed to be dragged::
Alexander Kryklia committed
257 258 259 260 261 262 263 264

    {
            'draggables': ['draggable_1', 'draggable_1'],
            'targets': ['target3', 'target6', 'target9'],
            'rule': 'anyof+number'
    }


Alexander Kryklia committed
265 266 267
* Note, that in using rule 'exact', one does not need 'number', because you can't recognize from user interface which reusable draggable is on which target. Absurd example::

    {
Alexander Kryklia committed
268 269 270 271 272 273
            'draggables': ['draggable_1', 'draggable_1', 'draggable_2'],
            'targets': ['target3', 'target6', 'target9'],
            'rule': 'exact'
    }


Alexander Kryklia committed
274 275
    Correct handling of this example is to create different rules for draggable_1 and
    draggable_2
Alexander Kryklia committed
276

Alexander Kryklia committed
277
* For 'unordered_equal' (or 'exact' too) we don't need 'number' if you have only same draggable in group, as targets length will provide constraint for the number of draggables::
Alexander Kryklia committed
278 279 280 281 282 283 284 285

    {
            'draggables': ['draggable_1'],
            'targets': ['target3', 'target6', 'target9'],
            'rule': 'unordered_equal'
    }


Alexander Kryklia committed
286 287 288
    This means that only three draggaggables 'draggable_1' can be dragged.

* But if you have more that one different reusable draggable in list, you may use 'number' rule::
Alexander Kryklia committed
289 290 291 292 293 294 295 296

    {
            'draggables': ['draggable_1', 'draggable_1', 'draggable_2'],
            'targets': ['target3', 'target6', 'target9'],
            'rule': 'unordered_equal+number'
    }


Alexander Kryklia committed
297 298
    If not use number, draggables list will be setted to  ['draggable_1', 'draggable_2']

Alexander Kryklia committed
299 300 301



Alexander Kryklia committed
302 303 304
Logic flow
----------

305 306
(Click on image to see full size version.)

Alexander Kryklia committed
307
.. image:: draganddrop_logic_flow.png
308 309
    :width: 100%
    :target: _images/draganddrop_logic_flow.png
Alexander Kryklia committed
310 311


312 313 314
Example
=======

315 316
Examples of draggables that can't be reused
-------------------------------------------
317

318
.. literalinclude:: drag-n-drop-demo.xml
319 320 321 322 323

Draggables can be reused
------------------------

.. literalinclude:: drag-n-drop-demo2.xml