Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
P
problem-builder
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
problem-builder
Commits
3296ca71
Commit
3296ca71
authored
Oct 19, 2015
by
Tim Krones
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Extend tests.
parent
c2fb804e
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
202 additions
and
16 deletions
+202
-16
problem_builder/plot.py
+14
-4
problem_builder/tests/integration/test_step_builder.py
+187
-11
problem_builder/tests/integration/xml_templates/step_builder_plot.xml
+1
-1
No files found.
problem_builder/plot.py
View file @
3296ca71
...
@@ -186,7 +186,6 @@ class PlotBlock(StudioEditableXBlockMixin, StudioContainerWithNestedXBlocksMixin
...
@@ -186,7 +186,6 @@ class PlotBlock(StudioEditableXBlockMixin, StudioContainerWithNestedXBlocksMixin
return
claims
return
claims
def
_get_default_response
(
self
,
question
,
question_id
):
def
_get_default_response
(
self
,
question
,
question_id
):
from
.tasks
import
_get_answer
# Import here to avoid circular dependency
# 1. Obtain block_type for question
# 1. Obtain block_type for question
question_type
=
question
.
scope_ids
.
block_type
question_type
=
question
.
scope_ids
.
block_type
# 2. Obtain latest submission for question
# 2. Obtain latest submission for question
...
@@ -200,11 +199,10 @@ class PlotBlock(StudioEditableXBlockMixin, StudioContainerWithNestedXBlocksMixin
...
@@ -200,11 +199,10 @@ class PlotBlock(StudioEditableXBlockMixin, StudioContainerWithNestedXBlocksMixin
# 3. Extract response from latest submission for question
# 3. Extract response from latest submission for question
answer_cache
=
{}
answer_cache
=
{}
for
submission
in
submissions
:
for
submission
in
submissions
:
answer
=
_get_answer
(
question
,
submission
,
answer_cache
)
answer
=
self
.
_get_answer
(
question
,
submission
,
answer_cache
)
return
int
(
answer
)
return
int
(
answer
)
def
_get_average_response
(
self
,
question
,
question_id
):
def
_get_average_response
(
self
,
question
,
question_id
):
from
.tasks
import
_get_answer
# Import here to avoid circular dependency
# 1. Obtain block_type for question
# 1. Obtain block_type for question
question_type
=
question
.
scope_ids
.
block_type
question_type
=
question
.
scope_ids
.
block_type
# 2. Obtain latest submissions for question
# 2. Obtain latest submissions for question
...
@@ -214,13 +212,25 @@ class PlotBlock(StudioEditableXBlockMixin, StudioContainerWithNestedXBlocksMixin
...
@@ -214,13 +212,25 @@ class PlotBlock(StudioEditableXBlockMixin, StudioContainerWithNestedXBlocksMixin
response_total
=
0
response_total
=
0
num_submissions
=
0
# Can't use len(submissions) because submissions is a generator
num_submissions
=
0
# Can't use len(submissions) because submissions is a generator
for
submission
in
submissions
:
for
submission
in
submissions
:
answer
=
_get_answer
(
question
,
submission
,
answer_cache
)
answer
=
self
.
_get_answer
(
question
,
submission
,
answer_cache
)
response_total
+=
int
(
answer
)
response_total
+=
int
(
answer
)
num_submissions
+=
1
num_submissions
+=
1
# 4. Calculate average response for question
# 4. Calculate average response for question
if
num_submissions
:
if
num_submissions
:
return
response_total
/
float
(
num_submissions
)
return
response_total
/
float
(
num_submissions
)
def
_get_answer
(
self
,
block
,
submission
,
answer_cache
):
"""
Return answer associated with `submission` to `block`.
`answer_cache` is a dict that is reset for each block.
"""
answer
=
submission
[
'answer'
]
# Convert from answer ID to answer label
if
answer
not
in
answer_cache
:
answer_cache
[
answer
]
=
block
.
get_submission_display
(
answer
)
return
answer_cache
[
answer
]
def
default_claims_json
(
self
):
def
default_claims_json
(
self
):
return
json
.
dumps
(
self
.
default_claims
)
return
json
.
dumps
(
self
.
default_claims
)
...
...
problem_builder/tests/integration/test_step_builder.py
View file @
3296ca71
...
@@ -3,11 +3,62 @@ from ddt import ddt, data
...
@@ -3,11 +3,62 @@ from ddt import ddt, data
from
workbench.runtime
import
WorkbenchRuntime
from
workbench.runtime
import
WorkbenchRuntime
from
.base_test
import
CORRECT
,
INCORRECT
,
PARTIAL
,
MentoringAssessmentBaseTest
,
GetChoices
from
.base_test
import
CORRECT
,
INCORRECT
,
PARTIAL
,
MentoringAssessmentBaseTest
,
GetChoices
from
.test_dashboard
import
MockSubmissionsAPI
class
ExtendedMockSubmissionsAPI
(
MockSubmissionsAPI
):
def
get_all_submissions
(
self
,
course_key_str
,
block_id
,
block_type
):
return
(
submission
for
submission
in
self
.
submissions
.
values
()
if
submission
[
'student_item'
][
'item_id'
]
==
block_id
)
@ddt
@ddt
class
StepBuilderTest
(
MentoringAssessmentBaseTest
):
class
StepBuilderTest
(
MentoringAssessmentBaseTest
):
def
setUp
(
self
):
super
(
StepBuilderTest
,
self
)
.
setUp
()
mock_submissions_api
=
ExtendedMockSubmissionsAPI
()
patches
=
(
(
"problem_builder.plot.PlotBlock.course_key_str"
,
property
(
lambda
block
:
"course_id"
)
),
(
"problem_builder.plot.sub_api"
,
mock_submissions_api
),
(
"problem_builder.mcq.sub_api"
,
mock_submissions_api
),
(
"problem_builder.sub_api.SubmittingXBlockMixin.student_item_key"
,
property
(
lambda
block
:
dict
(
student_id
=
"student_id"
,
course_id
=
"course_id"
,
item_id
=
block
.
scope_ids
.
usage_id
,
item_type
=
"pb-rating"
)
)
),
)
for
p
in
patches
:
patcher
=
patch
(
*
p
)
patcher
.
start
()
self
.
addCleanup
(
patcher
.
stop
)
runtime_patcher
=
patch
(
'workbench.runtime.WorkbenchRuntime.anonymous_student_id'
,
property
(
lambda
runtime
:
"student_id"
),
create
=
True
)
runtime_patcher
.
start
()
self
.
addCleanup
(
runtime_patcher
.
stop
)
def
freeform_answer
(
self
,
number
,
step_builder
,
controls
,
text_input
,
result
,
saved_value
=
""
,
last
=
False
):
def
freeform_answer
(
self
,
number
,
step_builder
,
controls
,
text_input
,
result
,
saved_value
=
""
,
last
=
False
):
self
.
expect_question_visible
(
number
,
step_builder
)
self
.
expect_question_visible
(
number
,
step_builder
)
...
@@ -536,13 +587,13 @@ class StepBuilderTest(MentoringAssessmentBaseTest):
...
@@ -536,13 +587,13 @@ class StepBuilderTest(MentoringAssessmentBaseTest):
points
=
step_builder
.
find_elements_by_css_selector
(
"circle"
)
points
=
step_builder
.
find_elements_by_css_selector
(
"circle"
)
self
.
assertEquals
(
points
,
[])
self
.
assertEquals
(
points
,
[])
def
check_quadrant_labels
(
self
,
step_builder
,
controls
,
hidden
):
def
check_quadrant_labels
(
self
,
step_builder
,
plot_controls
,
hidden
,
labels
=
[
'Q1'
,
'Q2'
,
'Q3'
,
'Q4'
]
):
quadrant_labels
=
step_builder
.
find_elements_by_css_selector
(
".quadrant-label"
)
quadrant_labels
=
step_builder
.
find_elements_by_css_selector
(
".quadrant-label"
)
quadrants_button_border_colors
=
[
quadrants_button_border_colors
=
[
controls
.
quadrants_button
.
value_of_css_property
(
'border-top-color'
),
plot_
controls
.
quadrants_button
.
value_of_css_property
(
'border-top-color'
),
controls
.
quadrants_button
.
value_of_css_property
(
'border-right-color'
),
plot_
controls
.
quadrants_button
.
value_of_css_property
(
'border-right-color'
),
controls
.
quadrants_button
.
value_of_css_property
(
'border-bottom-color'
),
plot_
controls
.
quadrants_button
.
value_of_css_property
(
'border-bottom-color'
),
controls
.
quadrants_button
.
value_of_css_property
(
'border-left-color'
),
plot_
controls
.
quadrants_button
.
value_of_css_property
(
'border-left-color'
),
]
]
if
hidden
:
if
hidden
:
self
.
assertEquals
(
quadrant_labels
,
[])
self
.
assertEquals
(
quadrant_labels
,
[])
...
@@ -550,12 +601,43 @@ class StepBuilderTest(MentoringAssessmentBaseTest):
...
@@ -550,12 +601,43 @@ class StepBuilderTest(MentoringAssessmentBaseTest):
self
.
assertTrue
(
all
(
bc
==
'rgba(255, 0, 0, 1)'
for
bc
in
quadrants_button_border_colors
))
self
.
assertTrue
(
all
(
bc
==
'rgba(255, 0, 0, 1)'
for
bc
in
quadrants_button_border_colors
))
else
:
else
:
self
.
assertEquals
(
len
(
quadrant_labels
),
4
)
self
.
assertEquals
(
len
(
quadrant_labels
),
4
)
self
.
assertEquals
(
set
(
label
.
text
for
label
in
quadrant_labels
),
set
(
[
'Q1'
,
'Q2'
,
'Q3'
,
'Q4'
]
))
self
.
assertEquals
(
set
(
label
.
text
for
label
in
quadrant_labels
),
set
(
labels
))
# rgba(0, 128, 0, 1): "green"
# rgba(0, 128, 0, 1): "green"
self
.
assertTrue
(
all
(
bc
==
'rgba(0, 128, 0, 1)'
for
bc
in
quadrants_button_border_colors
))
self
.
assertTrue
(
all
(
bc
==
'rgba(0, 128, 0, 1)'
for
bc
in
quadrants_button_border_colors
))
def
click_default_button
(
self
,
plot_controls
,
overlay_on
,
color_on
=
'rgba(0, 128, 0, 1)'
,
color_off
=
'rgba(237, 237, 237, 1)'
):
plot_controls
.
default_button
.
click
()
default_button_border_colors
=
[
plot_controls
.
default_button
.
value_of_css_property
(
'border-top-color'
),
plot_controls
.
default_button
.
value_of_css_property
(
'border-right-color'
),
plot_controls
.
default_button
.
value_of_css_property
(
'border-bottom-color'
),
plot_controls
.
default_button
.
value_of_css_property
(
'border-left-color'
),
]
if
overlay_on
:
self
.
assertTrue
(
all
(
bc
==
color_on
for
bc
in
default_button_border_colors
))
else
:
self
.
assertTrue
(
all
(
bc
==
color_off
for
bc
in
default_button_border_colors
))
def
click_average_button
(
self
,
plot_controls
,
overlay_on
,
color_on
=
'rgba(0, 0, 255, 1)'
,
color_off
=
'rgba(237, 237, 237, 1)'
):
plot_controls
.
average_button
.
click
()
average_button_border_colors
=
[
plot_controls
.
average_button
.
value_of_css_property
(
'border-top-color'
),
plot_controls
.
average_button
.
value_of_css_property
(
'border-right-color'
),
plot_controls
.
average_button
.
value_of_css_property
(
'border-bottom-color'
),
plot_controls
.
average_button
.
value_of_css_property
(
'border-left-color'
),
]
if
overlay_on
:
self
.
assertTrue
(
all
(
bc
==
color_on
for
bc
in
average_button_border_colors
))
else
:
self
.
assertTrue
(
all
(
bc
==
color_off
for
bc
in
average_button_border_colors
))
def
test_empty_plot
(
self
):
def
test_empty_plot
(
self
):
step_builder
,
controls
=
self
.
load_assessment_scenario
(
"step_builder_plot_defaults.xml"
,
{})
step_builder
,
controls
=
self
.
load_assessment_scenario
(
"step_builder_plot_defaults.xml"
,
{})
# Step 1: Questions
# Step 1: Questions
# Provide first rating
# Provide first rating
self
.
answer_rating_question
(
1
,
1
,
step_builder
,
"How much do you agree?"
,
"1 - Disagree"
)
self
.
answer_rating_question
(
1
,
1
,
step_builder
,
"How much do you agree?"
,
"1 - Disagree"
)
...
@@ -565,23 +647,117 @@ class StepBuilderTest(MentoringAssessmentBaseTest):
...
@@ -565,23 +647,117 @@ class StepBuilderTest(MentoringAssessmentBaseTest):
self
.
submit_and_go_to_next_step
(
controls
)
self
.
submit_and_go_to_next_step
(
controls
)
# Step 2: Plot
# Step 2: Plot
# Check if plot is empty (default overlay on, average overlay off)
# Check if plot is empty
initially
(default overlay on, average overlay off)
self
.
plot_empty
(
step_builder
)
self
.
plot_empty
(
step_builder
)
# Obtain references to plot controls
# Obtain references to plot controls
plot_controls
=
self
.
plot_controls
(
step_builder
)
plot_controls
=
self
.
plot_controls
(
step_builder
)
# Check if plot is empty (default overlay off, average overlay off)
# Check if plot is empty (default overlay off, average overlay off)
plot_controls
.
default_button
.
click
(
)
self
.
click_default_button
(
plot_controls
,
overlay_on
=
False
)
self
.
plot_empty
(
step_builder
)
self
.
plot_empty
(
step_builder
)
# Check if plot is empty (default overlay off, average overlay on)
# Check if plot is empty (default overlay off, average overlay on)
plot_controls
.
average_button
.
click
(
)
self
.
click_average_button
(
plot_controls
,
overlay_on
=
True
)
self
.
plot_empty
(
step_builder
)
self
.
plot_empty
(
step_builder
)
# Check if plot is empty (default overlay on, average overlay on)
# Check if plot is empty (default overlay on, average overlay on)
plot_controls
.
default_button
.
click
()
self
.
click_default_button
(
plot_controls
,
overlay_on
=
True
)
self
.
plot_empty
(
step_builder
)
# Check if plot is empty (default overlay on, average overlay off)
self
.
click_average_button
(
plot_controls
,
overlay_on
=
False
)
self
.
plot_empty
(
step_builder
)
self
.
plot_empty
(
step_builder
)
# Check quadrant labels
# Check quadrant labels
self
.
check_quadrant_labels
(
step_builder
,
plot_controls
,
hidden
=
True
)
self
.
check_quadrant_labels
(
step_builder
,
plot_controls
,
hidden
=
True
)
plot_controls
.
quadrants_button
.
click
()
plot_controls
.
quadrants_button
.
click
()
self
.
check_quadrant_labels
(
step_builder
,
plot_controls
,
hidden
=
False
)
self
.
check_quadrant_labels
(
step_builder
,
plot_controls
,
hidden
=
False
)
def
check_overlays
(
self
,
step_builder
,
total_num_points
,
overlays
):
points
=
step_builder
.
find_elements_by_css_selector
(
"circle"
)
self
.
assertEquals
(
len
(
points
),
total_num_points
)
for
overlay
in
overlays
:
# Check if correct number of points is present
points
=
step_builder
.
find_elements_by_css_selector
(
overlay
[
'selector'
])
self
.
assertEquals
(
len
(
points
),
overlay
[
'num_points'
])
# Check point colors
point_colors
=
[
point
.
value_of_css_property
(
'fill'
)
for
point
in
points
]
self
.
assertTrue
(
all
(
pc
==
overlay
[
'point_color'
]
for
pc
in
point_colors
))
# Check point titles
point_titles
=
set
([
point
.
get_attribute
(
'title'
)
for
point
in
points
])
self
.
assertEquals
(
point_titles
,
set
(
overlay
[
'titles'
]))
# Check positions
point_positions
=
set
([
(
point
.
get_attribute
(
'cx'
),
point
.
get_attribute
(
'cy'
))
for
point
in
points
])
self
.
assertEquals
(
point_positions
,
set
(
overlay
[
'positions'
]))
def
test_plot
(
self
):
def
test_plot
(
self
):
pass
step_builder
,
controls
=
self
.
load_assessment_scenario
(
"step_builder_plot.xml"
,
{})
# Step 1: Questions
# Provide first rating
self
.
answer_rating_question
(
1
,
1
,
step_builder
,
"How much do you agree?"
,
"1 - Disagree"
)
# Provide second rating
self
.
answer_rating_question
(
1
,
2
,
step_builder
,
"How important do you think this is?"
,
"5 - Very important"
)
# Advance
self
.
submit_and_go_to_next_step
(
controls
)
# Step 2: Questions
# Provide first rating
self
.
answer_rating_question
(
2
,
1
,
step_builder
,
"How much do you agree?"
,
"5 - Agree"
)
# Provide second rating
self
.
answer_rating_question
(
2
,
2
,
step_builder
,
"How important do you think this is?"
,
"1 - Not important"
)
# Advance
self
.
submit_and_go_to_next_step
(
controls
)
# Step 2: Plot
# Obtain references to plot controls
plot_controls
=
self
.
plot_controls
(
step_builder
)
# Overlay data
default_overlay
=
{
'selector'
:
'.claim-default'
,
'num_points'
:
2
,
'point_color'
:
'rgb(255, 165, 0)'
,
# orange
'titles'
:
[
'2 + 2 = 5: 1, 5'
,
'The answer to everything is 42: 5, 1'
],
'positions'
:
[
(
'20'
,
'396'
),
# Values computed according to xScale and yScale (cf. plot.js)
(
'4'
,
'380'
),
# Values computed according to xScale and yScale (cf. plot.js)
],
}
average_overlay
=
{
'selector'
:
'.claim-average'
,
'num_points'
:
2
,
'point_color'
:
'rgb(128, 0, 128)'
,
# purple
'titles'
:
[
'2 + 2 = 5: 1, 5'
,
'The answer to everything is 42: 5, 1'
],
'positions'
:
[
(
'20'
,
'396'
),
# Values computed according to xScale and yScale (cf. plot.js)
(
'4'
,
'380'
),
# Values computed according to xScale and yScale (cf. plot.js)
],
}
# Check if plot shows correct overlay(s) initially (default overlay on, average overlay off)
self
.
check_overlays
(
step_builder
,
total_num_points
=
2
,
overlays
=
[
default_overlay
])
# Check if plot shows correct overlay(s) (default overlay on, average overlay on)
self
.
click_average_button
(
plot_controls
,
overlay_on
=
True
,
color_on
=
'rgba(128, 0, 128, 1)'
)
# purple
self
.
check_overlays
(
step_builder
,
4
,
overlays
=
[
default_overlay
,
average_overlay
])
# Check if plot shows correct overlay(s) (default overlay off, average overlay on)
self
.
click_default_button
(
plot_controls
,
overlay_on
=
False
)
self
.
check_overlays
(
step_builder
,
2
,
overlays
=
[
average_overlay
])
# Check if plot shows correct overlay(s) (default overlay off, average overlay off)
self
.
click_average_button
(
plot_controls
,
overlay_on
=
False
)
self
.
plot_empty
(
step_builder
)
# Check if plot shows correct overlay(s) (default overlay on, average overlay off)
self
.
click_default_button
(
plot_controls
,
overlay_on
=
True
,
color_on
=
'rgba(255, 165, 0, 1)'
)
# orange
self
.
check_overlays
(
step_builder
,
2
,
overlays
=
[
default_overlay
])
# Check quadrant labels
self
.
check_quadrant_labels
(
step_builder
,
plot_controls
,
hidden
=
True
)
plot_controls
.
quadrants_button
.
click
()
self
.
check_quadrant_labels
(
step_builder
,
plot_controls
,
hidden
=
False
,
labels
=
[
'Custom Q1 label'
,
'Custom Q2 label'
,
'Custom Q3 label'
,
'Custom Q4 label'
]
)
problem_builder/tests/integration/xml_templates/step_builder_plot.xml
View file @
3296ca71
...
@@ -38,7 +38,7 @@
...
@@ -38,7 +38,7 @@
q2_label=
"Custom Q2 label"
q2_label=
"Custom Q2 label"
q3_label=
"Custom Q3 label"
q3_label=
"Custom Q3 label"
q4_label=
"Custom Q4 label"
q4_label=
"Custom Q4 label"
claims=
"2 + 2 = 5, rating_1_1, rating_1_2
\n
The answer to everything is 42, rating_2_1, rating_2_2"
>
claims=
"2 + 2 = 5, rating_1_1, rating_1_2
The answer to everything is 42, rating_2_1, rating_2_2"
>
</sb-plot>
</sb-plot>
</sb-step>
</sb-step>
...
...
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