Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
X
xblock-drag-and-drop-v2
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
OpenEdx
xblock-drag-and-drop-v2
Commits
d6b36969
Commit
d6b36969
authored
Sep 07, 2016
by
Jillian Vogel
Committed by
GitHub
Sep 07, 2016
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #97 from arbrandes/SOL-1805
[SOL-1805] Add "item" field to item.dropped event
parents
cab91d99
c78dbc6f
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
184 additions
and
32 deletions
+184
-32
README.md
+4
-0
drag_and_drop_v2/default_data.py
+11
-5
drag_and_drop_v2/drag_and_drop_v2.py
+5
-0
tests/integration/data/test_item_dropped.json
+49
-0
tests/integration/test_base.py
+20
-12
tests/integration/test_events.py
+74
-2
tests/integration/test_interaction.py
+21
-13
No files found.
README.md
View file @
d6b36969
...
...
@@ -294,8 +294,10 @@ Example ("common" fields that are not interesting in this context have been left
...
"event": {
"is_correct": true, -- Whether the draggable item has been placed in the correct location.
"item": "Goes to the top", -- Name, or in the absence thereof, image URL of the draggable item.
"item_id": 0, -- ID of the draggable item.
"location": "The Top Zone", -- Name of the location the item was dragged to.
"location_id": 1, -- ID of the location the item was dragged to.
},
"event_source": "server", -- Common field, contains event source.
"event_type": "edx.drag_and_drop_v2.dropped", -- Common field, contains event name.
...
...
@@ -316,6 +318,8 @@ Real event example (taken from a devstack):
"event": {
"is_correct": true,
"location": "The Top Zone",
"location_id": 1,
"item": "Goes to the top",
"item_id": 0,
},
"event_source": "server",
...
...
drag_and_drop_v2/default_data.py
View file @
d6b36969
...
...
@@ -24,6 +24,12 @@ ITEM_INCORRECT_FEEDBACK = _("No, this item does not belong here. Try again.")
ITEM_NO_ZONE_FEEDBACK
=
_
(
"You silly, there are no zones for this one."
)
ITEM_ANY_ZONE_FEEDBACK
=
_
(
"Of course it goes here! It goes anywhere!"
)
ITEM_TOP_ZONE_NAME
=
_
(
"Goes to the top"
)
ITEM_MIDDLE_ZONE_NAME
=
_
(
"Goes to the middle"
)
ITEM_BOTTOM_ZONE_NAME
=
_
(
"Goes to the bottom"
)
ITEM_ANY_ZONE_NAME
=
_
(
"Goes anywhere"
)
ITEM_NO_ZONE_NAME
=
_
(
"I don't belong anywhere"
)
START_FEEDBACK
=
_
(
"Drag the items onto the image above."
)
FINISH_FEEDBACK
=
_
(
"Good work! You have completed this drag and drop problem."
)
...
...
@@ -63,7 +69,7 @@ DEFAULT_DATA = {
],
"items"
:
[
{
"displayName"
:
_
(
"Goes to the top"
)
,
"displayName"
:
ITEM_TOP_ZONE_NAME
,
"feedback"
:
{
"incorrect"
:
ITEM_INCORRECT_FEEDBACK
,
"correct"
:
ITEM_CORRECT_FEEDBACK
.
format
(
zone
=
TOP_ZONE_TITLE
)
...
...
@@ -75,7 +81,7 @@ DEFAULT_DATA = {
"id"
:
0
,
},
{
"displayName"
:
_
(
"Goes to the middle"
)
,
"displayName"
:
ITEM_MIDDLE_ZONE_NAME
,
"feedback"
:
{
"incorrect"
:
ITEM_INCORRECT_FEEDBACK
,
"correct"
:
ITEM_CORRECT_FEEDBACK
.
format
(
zone
=
MIDDLE_ZONE_TITLE
)
...
...
@@ -87,7 +93,7 @@ DEFAULT_DATA = {
"id"
:
1
,
},
{
"displayName"
:
_
(
"Goes to the bottom"
)
,
"displayName"
:
ITEM_BOTTOM_ZONE_NAME
,
"feedback"
:
{
"incorrect"
:
ITEM_INCORRECT_FEEDBACK
,
"correct"
:
ITEM_CORRECT_FEEDBACK
.
format
(
zone
=
BOTTOM_ZONE_TITLE
)
...
...
@@ -99,7 +105,7 @@ DEFAULT_DATA = {
"id"
:
2
,
},
{
"displayName"
:
_
(
"Goes anywhere"
)
,
"displayName"
:
ITEM_ANY_ZONE_NAME
,
"feedback"
:
{
"incorrect"
:
""
,
"correct"
:
ITEM_ANY_ZONE_FEEDBACK
...
...
@@ -113,7 +119,7 @@ DEFAULT_DATA = {
"id"
:
3
},
{
"displayName"
:
_
(
"I don't belong anywhere"
)
,
"displayName"
:
ITEM_NO_ZONE_NAME
,
"feedback"
:
{
"incorrect"
:
ITEM_NO_ZONE_FEEDBACK
,
"correct"
:
""
...
...
drag_and_drop_v2/drag_and_drop_v2.py
View file @
d6b36969
...
...
@@ -663,7 +663,12 @@ class DragAndDropBlock(XBlock, XBlockWithSettingsMixin, ThemableXBlockMixin):
# attempt should already be validated here - not doing the check for existing zone again
zone
=
self
.
_get_zone_by_uid
(
attempt
[
'zone'
])
item_label
=
item
.
get
(
"displayName"
)
if
not
item_label
:
item_label
=
item
.
get
(
"imageURL"
)
self
.
runtime
.
publish
(
self
,
'edx.drag_and_drop_v2.item.dropped'
,
{
'item'
:
item_label
,
'item_id'
:
item
[
'id'
],
'location'
:
zone
.
get
(
"title"
),
'location_id'
:
zone
.
get
(
"uid"
),
...
...
tests/integration/data/test_item_dropped.json
0 → 100644
View file @
d6b36969
{
"zones"
:
[
{
"width"
:
200
,
"title"
:
"Zone 1"
,
"height"
:
100
,
"y"
:
"200"
,
"x"
:
"100"
,
"uid"
:
"zone-1"
},
{
"width"
:
200
,
"title"
:
"Zone 2"
,
"height"
:
100
,
"y"
:
0
,
"x"
:
0
,
"uid"
:
"zone-2"
}
],
"items"
:
[
{
"displayName"
:
"Has name"
,
"feedback"
:
{
"incorrect"
:
"No"
,
"correct"
:
"Yes"
},
"zones"
:
[
"zone-1"
],
"imageURL"
:
""
,
"id"
:
0
},
{
"displayName"
:
""
,
"feedback"
:
{
"incorrect"
:
"No"
,
"correct"
:
"Yes"
},
"zone"
:
"zone-2"
,
"imageURL"
:
"https://placehold.it/100x100"
,
"id"
:
1
}
],
"feedback"
:
{
"start"
:
"Start feedback"
,
"finish"
:
"Finish feedback"
}
}
tests/integration/test_base.py
View file @
d6b36969
...
...
@@ -7,6 +7,7 @@ from xml.sax.saxutils import escape
from
selenium.webdriver
import
ActionChains
from
selenium.webdriver.common.keys
import
Keys
from
selenium.webdriver.support.ui
import
WebDriverWait
from
collections
import
namedtuple
from
bok_choy.promise
import
EmptyPromise
from
workbench
import
scenarios
...
...
@@ -20,6 +21,8 @@ from drag_and_drop_v2.default_data import (
DEFAULT_DATA
,
START_FEEDBACK
,
FINISH_FEEDBACK
,
TOP_ZONE_ID
,
TOP_ZONE_TITLE
,
MIDDLE_ZONE_ID
,
MIDDLE_ZONE_TITLE
,
BOTTOM_ZONE_ID
,
BOTTOM_ZONE_TITLE
,
ITEM_CORRECT_FEEDBACK
,
ITEM_INCORRECT_FEEDBACK
,
ITEM_ANY_ZONE_FEEDBACK
,
ITEM_NO_ZONE_FEEDBACK
,
ITEM_TOP_ZONE_NAME
,
ITEM_MIDDLE_ZONE_NAME
,
ITEM_BOTTOM_ZONE_NAME
,
ITEM_ANY_ZONE_NAME
,
ITEM_NO_ZONE_NAME
,
)
# Globals ###########################################################
...
...
@@ -29,13 +32,18 @@ loader = ResourceLoader(__name__)
# Classes ###########################################################
class
ItemDefinition
(
object
):
def
__init__
(
self
,
item_id
,
zone_ids
,
zone_title
,
feedback_positive
,
feedback_negative
):
self
.
feedback_negative
=
feedback_negative
self
.
feedback_positive
=
feedback_positive
self
.
zone_ids
=
zone_ids
self
.
zone_title
=
zone_title
self
.
item_id
=
item_id
ItemDefinition
=
namedtuple
(
# pylint: disable=invalid-name
"ItemDefinition"
,
[
"item_id"
,
"item_name"
,
"image_url"
,
"zone_ids"
,
"zone_title"
,
"feedback_positive"
,
"feedback_negative"
,
]
)
class
BaseIntegrationTest
(
SeleniumBaseTest
):
...
...
@@ -180,22 +188,22 @@ class DefaultDataTestMixin(object):
items_map
=
{
0
:
ItemDefinition
(
0
,
[
TOP_ZONE_ID
],
TOP_ZONE_TITLE
,
0
,
ITEM_TOP_ZONE_NAME
,
""
,
[
TOP_ZONE_ID
],
TOP_ZONE_TITLE
,
ITEM_CORRECT_FEEDBACK
.
format
(
zone
=
TOP_ZONE_TITLE
),
ITEM_INCORRECT_FEEDBACK
),
1
:
ItemDefinition
(
1
,
[
MIDDLE_ZONE_ID
],
MIDDLE_ZONE_TITLE
,
1
,
ITEM_MIDDLE_ZONE_NAME
,
""
,
[
MIDDLE_ZONE_ID
],
MIDDLE_ZONE_TITLE
,
ITEM_CORRECT_FEEDBACK
.
format
(
zone
=
MIDDLE_ZONE_TITLE
),
ITEM_INCORRECT_FEEDBACK
),
2
:
ItemDefinition
(
2
,
[
BOTTOM_ZONE_ID
],
BOTTOM_ZONE_TITLE
,
2
,
ITEM_BOTTOM_ZONE_NAME
,
""
,
[
BOTTOM_ZONE_ID
],
BOTTOM_ZONE_TITLE
,
ITEM_CORRECT_FEEDBACK
.
format
(
zone
=
BOTTOM_ZONE_TITLE
),
ITEM_INCORRECT_FEEDBACK
),
3
:
ItemDefinition
(
3
,
[
MIDDLE_ZONE_ID
,
TOP_ZONE_ID
,
BOTTOM_ZONE_ID
],
MIDDLE_ZONE_TITLE
,
3
,
ITEM_ANY_ZONE_NAME
,
""
,
[
MIDDLE_ZONE_ID
,
TOP_ZONE_ID
,
BOTTOM_ZONE_ID
],
MIDDLE_ZONE_TITLE
,
ITEM_ANY_ZONE_FEEDBACK
,
ITEM_INCORRECT_FEEDBACK
),
4
:
ItemDefinition
(
4
,
[],
None
,
""
,
ITEM_NO_ZONE_FEEDBACK
),
4
:
ItemDefinition
(
4
,
ITEM_NO_ZONE_NAME
,
""
,
[],
None
,
""
,
ITEM_NO_ZONE_FEEDBACK
),
}
all_zones
=
[
...
...
tests/integration/test_events.py
View file @
d6b36969
from
ddt
import
data
,
ddt
,
unpack
from
mock
import
Mock
,
patch
from
selenium.webdriver.common.keys
import
Keys
from
workbench.runtime
import
WorkbenchRuntime
from
drag_and_drop_v2.default_data
import
(
TOP_ZONE_TITLE
,
TOP_ZONE_ID
,
MIDDLE_ZONE_TITLE
,
MIDDLE_ZONE_ID
,
ITEM_CORRECT_FEEDBACK
,
ITEM_INCORRECT_FEEDBACK
TOP_ZONE_TITLE
,
TOP_ZONE_ID
,
MIDDLE_ZONE_TITLE
,
MIDDLE_ZONE_ID
,
ITEM_CORRECT_FEEDBACK
,
ITEM_INCORRECT_FEEDBACK
,
ITEM_TOP_ZONE_NAME
,
ITEM_MIDDLE_ZONE_NAME
,
)
from
tests.integration.test_base
import
BaseIntegrationTest
,
DefaultDataTestMixin
,
InteractionTestBase
from
tests.integration.test_base
import
BaseIntegrationTest
,
DefaultDataTestMixin
,
InteractionTestBase
,
ItemDefinition
from
tests.integration.test_interaction
import
DefaultDataTestMixin
,
ParameterizedTestsMixin
from
tests.integration.test_interaction_assessment
import
DefaultAssessmentDataTestMixin
,
AssessmentTestMixin
...
...
@@ -44,6 +46,7 @@ class EventsFiredTest(DefaultDataTestMixin, ParameterizedTestsMixin, BaseEventsT
'name'
:
'edx.drag_and_drop_v2.item.dropped'
,
'data'
:
{
'is_correct'
:
True
,
'item'
:
ITEM_TOP_ZONE_NAME
,
'item_id'
:
0
,
'location'
:
TOP_ZONE_TITLE
,
'location_id'
:
TOP_ZONE_ID
,
...
...
@@ -95,6 +98,7 @@ class AssessmentEventsFiredTest(
'name'
:
'edx.drag_and_drop_v2.item.dropped'
,
'data'
:
{
'is_correct'
:
False
,
'item'
:
ITEM_TOP_ZONE_NAME
,
'item_id'
:
0
,
'location'
:
MIDDLE_ZONE_TITLE
,
'location_id'
:
MIDDLE_ZONE_ID
,
...
...
@@ -108,6 +112,7 @@ class AssessmentEventsFiredTest(
'name'
:
'edx.drag_and_drop_v2.item.dropped'
,
'data'
:
{
'is_correct'
:
False
,
'item'
:
ITEM_MIDDLE_ZONE_NAME
,
'item_id'
:
1
,
'location'
:
TOP_ZONE_TITLE
,
'location_id'
:
TOP_ZONE_ID
,
...
...
@@ -140,3 +145,70 @@ class AssessmentEventsFiredTest(
dummy
,
name
,
published_data
=
self
.
publish
.
call_args_list
[
index
][
0
]
self
.
assertEqual
(
name
,
event
[
'name'
])
self
.
assertEqual
(
published_data
,
event
[
'data'
])
@ddt
class
ItemDroppedEventTest
(
DefaultDataTestMixin
,
BaseEventsTests
):
"""
Test that the item.dropped event behaves properly.
"""
items_map
=
{
0
:
ItemDefinition
(
0
,
"Has name"
,
""
,
'zone-1'
,
"Zone 1"
,
"Yes"
,
"No"
),
1
:
ItemDefinition
(
1
,
""
,
"https://placehold.it/100x100"
,
'zone-2'
,
"Zone 2"
,
"Yes"
,
"No"
),
}
scenarios
=
(
(
[
'zone-1'
,
'zone-2'
],
[
{
'is_correct'
:
True
,
'item'
:
"Has name"
,
'item_id'
:
0
,
'location'
:
'Zone 1'
,
'location_id'
:
'zone-1'
},
{
'is_correct'
:
True
,
'item'
:
"https://placehold.it/100x100"
,
'item_id'
:
1
,
'location'
:
'Zone 2'
,
'location_id'
:
'zone-2'
}
],
),
(
[
'zone-2'
,
'zone-1'
],
[
{
'is_correct'
:
False
,
'item'
:
"Has name"
,
'item_id'
:
0
,
'location'
:
'Zone 2'
,
'location_id'
:
'zone-2'
},
{
'is_correct'
:
False
,
'item'
:
"https://placehold.it/100x100"
,
'item_id'
:
1
,
'location'
:
'Zone 1'
,
'location_id'
:
'zone-1'
}
],
),
)
def
_get_scenario_xml
(
self
):
return
self
.
_get_custom_scenario_xml
(
"data/test_item_dropped.json"
)
@data
(
*
scenarios
)
# pylint: disable=star-args
@unpack
def
test_item_dropped_event
(
self
,
placement
,
expected_events
):
for
i
,
zone
in
enumerate
(
placement
):
self
.
place_item
(
i
,
zone
,
Keys
.
RETURN
)
events
=
self
.
publish
.
call_args_list
event_name
=
'edx.drag_and_drop_v2.item.dropped'
published_events
=
[
event
[
0
][
2
]
for
event
in
events
if
event
[
0
][
1
]
==
event_name
]
self
.
assertEqual
(
published_events
,
expected_events
)
tests/integration/test_interaction.py
View file @
d6b36969
...
...
@@ -275,7 +275,15 @@ class StandardInteractionTest(DefaultDataTestMixin, InteractionTestBase, Paramet
class
MultipleValidOptionsInteractionTest
(
DefaultDataTestMixin
,
InteractionTestBase
,
BaseIntegrationTest
):
items_map
=
{
0
:
ItemDefinition
(
0
,
[
'zone-1'
,
'zone-2'
],
[
"Zone 1"
,
"Zone 2"
],
[
"Yes 1"
,
"Yes 1"
],
[
"No 1"
,
"No 1"
]),
0
:
ItemDefinition
(
0
,
"Item"
,
""
,
[
'zone-1'
,
'zone-2'
],
[
"Zone 1"
,
"Zone 2"
],
[
"Yes 1"
,
"Yes 1"
],
[
"No 1"
,
"No 1"
]
),
}
def
test_multiple_positive_feedback
(
self
):
...
...
@@ -334,9 +342,9 @@ class PreventSpaceBarScrollTest(DefaultDataTestMixin, InteractionTestBase, BaseI
class
CustomDataInteractionTest
(
StandardInteractionTest
):
items_map
=
{
0
:
ItemDefinition
(
0
,
[
'zone-1'
],
"Zone 1"
,
"Yes 1"
,
"No 1"
),
1
:
ItemDefinition
(
1
,
[
'zone-2'
],
"Zone 2"
,
"Yes 2"
,
"No 2"
),
2
:
ItemDefinition
(
2
,
[],
None
,
""
,
"No Zone for this"
)
0
:
ItemDefinition
(
0
,
"Item 0"
,
""
,
[
'zone-1'
],
"Zone 1"
,
"Yes 1"
,
"No 1"
),
1
:
ItemDefinition
(
1
,
"Item 1"
,
""
,
[
'zone-2'
],
"Zone 2"
,
"Yes 2"
,
"No 2"
),
2
:
ItemDefinition
(
2
,
"Item 2"
,
""
,
[],
None
,
""
,
"No Zone for this"
)
}
all_zones
=
[(
'zone-1'
,
'Zone 1'
),
(
'zone-2'
,
'Zone 2'
)]
...
...
@@ -352,9 +360,9 @@ class CustomDataInteractionTest(StandardInteractionTest):
class
CustomHtmlDataInteractionTest
(
StandardInteractionTest
):
items_map
=
{
0
:
ItemDefinition
(
0
,
[
'zone-1'
],
'Zone <i>1</i>'
,
"Yes <b>1</b>"
,
"No <b>1</b>"
),
1
:
ItemDefinition
(
1
,
[
'zone-2'
],
'Zone <b>2</b>'
,
"Yes <i>2</i>"
,
"No <i>2</i>"
),
2
:
ItemDefinition
(
2
,
[],
None
,
""
,
"No Zone for <i>X</i>"
)
0
:
ItemDefinition
(
0
,
"Item 0"
,
""
,
[
'zone-1'
],
'Zone <i>1</i>'
,
"Yes <b>1</b>"
,
"No <b>1</b>"
),
1
:
ItemDefinition
(
1
,
"Item 1"
,
""
,
[
'zone-2'
],
'Zone <b>2</b>'
,
"Yes <i>2</i>"
,
"No <i>2</i>"
),
2
:
ItemDefinition
(
2
,
"Item 2"
,
""
,
[],
None
,
""
,
"No Zone for <i>X</i>"
)
}
all_zones
=
[(
'zone-1'
,
'Zone 1'
),
(
'zone-2'
,
'Zone 2'
)]
...
...
@@ -377,14 +385,14 @@ class MultipleBlocksDataInteraction(ParameterizedTestsMixin, InteractionTestBase
item_maps
=
{
'block1'
:
{
0
:
ItemDefinition
(
0
,
[
'zone-1'
],
'Zone 1'
,
"Yes 1"
,
"No 1"
),
1
:
ItemDefinition
(
1
,
[
'zone-2'
],
'Zone 2'
,
"Yes 2"
,
"No 2"
),
2
:
ItemDefinition
(
2
,
[],
None
,
""
,
"No Zone for this"
)
0
:
ItemDefinition
(
0
,
"Item 0"
,
""
,
[
'zone-1'
],
'Zone 1'
,
"Yes 1"
,
"No 1"
),
1
:
ItemDefinition
(
1
,
"Item 1"
,
""
,
[
'zone-2'
],
'Zone 2'
,
"Yes 2"
,
"No 2"
),
2
:
ItemDefinition
(
2
,
"Item 2"
,
""
,
[],
None
,
""
,
"No Zone for this"
)
},
'block2'
:
{
10
:
ItemDefinition
(
10
,
[
'zone-51'
],
'Zone 51'
,
"Correct 1"
,
"Incorrect 1"
),
20
:
ItemDefinition
(
20
,
[
'zone-52'
],
'Zone 52'
,
"Correct 2"
,
"Incorrect 2"
),
30
:
ItemDefinition
(
30
,
[],
None
,
""
,
"No Zone for this"
)
10
:
ItemDefinition
(
10
,
"Item 10"
,
""
,
[
'zone-51'
],
'Zone 51'
,
"Correct 1"
,
"Incorrect 1"
),
20
:
ItemDefinition
(
20
,
"Item 20"
,
""
,
[
'zone-52'
],
'Zone 52'
,
"Correct 2"
,
"Incorrect 2"
),
30
:
ItemDefinition
(
30
,
"Item 30"
,
""
,
[],
None
,
""
,
"No Zone for this"
)
},
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment