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
318d8c04
Commit
318d8c04
authored
Oct 22, 2015
by
Braden MacDonald
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fix: Step Builder grades did not appear
parent
127c50db
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
124 additions
and
15 deletions
+124
-15
problem_builder/mentoring.py
+17
-15
problem_builder/tests/unit/test_common.py
+26
-0
problem_builder/tests/unit/test_problem_builder.py
+0
-0
problem_builder/tests/unit/test_step_builder.py
+33
-0
problem_builder/tests/unit/utils.py
+48
-0
No files found.
problem_builder/mentoring.py
View file @
318d8c04
...
@@ -99,6 +99,13 @@ class BaseMentoringBlock(
...
@@ -99,6 +99,13 @@ class BaseMentoringBlock(
scope
=
Scope
.
content
,
scope
=
Scope
.
content
,
enforce_type
=
True
enforce_type
=
True
)
)
weight
=
Float
(
display_name
=
_
(
"Weight"
),
help
=
_
(
"Defines the maximum total grade of the block."
),
default
=
1
,
scope
=
Scope
.
settings
,
enforce_type
=
True
)
# User state
# User state
num_attempts
=
Integer
(
num_attempts
=
Integer
(
...
@@ -109,6 +116,7 @@ class BaseMentoringBlock(
...
@@ -109,6 +116,7 @@ class BaseMentoringBlock(
)
)
has_children
=
True
has_children
=
True
has_score
=
True
# The Problem/Step Builder XBlocks produce scores. (Their children do not send scores to the LMS.)
icon_class
=
'problem'
icon_class
=
'problem'
block_settings_key
=
'mentoring'
block_settings_key
=
'mentoring'
...
@@ -197,8 +205,11 @@ class BaseMentoringBlock(
...
@@ -197,8 +205,11 @@ class BaseMentoringBlock(
Publish data for analytics purposes
Publish data for analytics purposes
"""
"""
event_type
=
data
.
pop
(
'event_type'
)
event_type
=
data
.
pop
(
'event_type'
)
self
.
runtime
.
publish
(
self
,
event_type
,
data
)
if
(
event_type
==
'grade'
):
# This handler can be called from the browser. Don't allow the browser to submit arbitrary grades ;-)
raise
JsonHandlerError
(
403
,
"Posting grade events from the browser is forbidden."
)
self
.
runtime
.
publish
(
self
,
event_type
,
data
)
return
{
'result'
:
'ok'
}
return
{
'result'
:
'ok'
}
def
author_preview_view
(
self
,
context
):
def
author_preview_view
(
self
,
context
):
...
@@ -214,6 +225,10 @@ class BaseMentoringBlock(
...
@@ -214,6 +225,10 @@ class BaseMentoringBlock(
self
.
include_theme_files
(
fragment
)
self
.
include_theme_files
(
fragment
)
return
fragment
return
fragment
def
max_score
(
self
):
""" Maximum score. We scale all scores to a maximum of 1.0 so this is always 1.0 """
return
1.0
class
MentoringBlock
(
BaseMentoringBlock
,
StudioContainerXBlockMixin
,
StepParentMixin
):
class
MentoringBlock
(
BaseMentoringBlock
,
StudioContainerXBlockMixin
,
StepParentMixin
):
"""
"""
...
@@ -262,13 +277,6 @@ class MentoringBlock(BaseMentoringBlock, StudioContainerXBlockMixin, StepParentM
...
@@ -262,13 +277,6 @@ class MentoringBlock(BaseMentoringBlock, StudioContainerXBlockMixin, StepParentM
)
)
# Settings
# Settings
weight
=
Float
(
display_name
=
_
(
"Weight"
),
help
=
_
(
"Defines the maximum total grade of the block."
),
default
=
1
,
scope
=
Scope
.
settings
,
enforce_type
=
True
)
display_name
=
String
(
display_name
=
String
(
display_name
=
_
(
"Title (Display name)"
),
display_name
=
_
(
"Title (Display name)"
),
help
=
_
(
"Title to display"
),
help
=
_
(
"Title to display"
),
...
@@ -323,8 +331,6 @@ class MentoringBlock(BaseMentoringBlock, StudioContainerXBlockMixin, StepParentM
...
@@ -323,8 +331,6 @@ class MentoringBlock(BaseMentoringBlock, StudioContainerXBlockMixin, StepParentM
'display_submit'
,
'feedback_label'
,
'weight'
,
'extended_feedback'
'display_submit'
,
'feedback_label'
,
'weight'
,
'extended_feedback'
)
)
has_score
=
True
@property
@property
def
is_assessment
(
self
):
def
is_assessment
(
self
):
""" Checks if mentoring XBlock is in assessment mode """
""" Checks if mentoring XBlock is in assessment mode """
...
@@ -377,10 +383,6 @@ class MentoringBlock(BaseMentoringBlock, StudioContainerXBlockMixin, StepParentM
...
@@ -377,10 +383,6 @@ class MentoringBlock(BaseMentoringBlock, StudioContainerXBlockMixin, StepParentM
return
Score
(
score
,
int
(
round
(
score
*
100
)),
correct
,
incorrect
,
partially_correct
)
return
Score
(
score
,
int
(
round
(
score
*
100
)),
correct
,
incorrect
,
partially_correct
)
def
max_score
(
self
):
""" Maximum score. We scale all scores to a maximum of 1.0 so this is always 1.0 """
return
1.0
def
student_view
(
self
,
context
):
def
student_view
(
self
,
context
):
# Migrate stored data if necessary
# Migrate stored data if necessary
self
.
migrate_fields
()
self
.
migrate_fields
()
...
@@ -848,7 +850,7 @@ class MentoringWithExplicitStepsBlock(BaseMentoringBlock, StudioContainerWithNes
...
@@ -848,7 +850,7 @@ class MentoringWithExplicitStepsBlock(BaseMentoringBlock, StudioContainerWithNes
enforce_type
=
True
enforce_type
=
True
)
)
editable_fields
=
(
'display_name'
,
'max_attempts'
,
'extended_feedback'
)
editable_fields
=
(
'display_name'
,
'max_attempts'
,
'extended_feedback'
,
'weight'
)
@lazy
@lazy
def
question_ids
(
self
):
def
question_ids
(
self
):
...
...
problem_builder/tests/unit/test_common.py
0 → 100644
View file @
318d8c04
"""
Tests common to Problem Builder and Step Builder
"""
import
ddt
import
unittest
from
problem_builder.mentoring
import
MentoringBlock
,
MentoringWithExplicitStepsBlock
from
xblock.core
import
XBlock
from
.utils
import
ScoresTestMixin
,
instantiate_block
@ddt.ddt
class
TestBuilderBlocks
(
ScoresTestMixin
,
unittest
.
TestCase
):
""" Unit tests for Problem Builder and Step Builder """
@ddt.data
(
MentoringBlock
,
MentoringWithExplicitStepsBlock
)
def
test_interface
(
self
,
block_cls
):
"""
Basic tests of the block's public interface.
"""
self
.
assertTrue
(
issubclass
(
block_cls
,
XBlock
))
self
.
assertTrue
(
block_cls
.
has_children
)
block
=
instantiate_block
(
block_cls
)
self
.
assertTrue
(
block
.
has_children
)
self
.
assert_produces_scores
(
block
)
problem_builder/tests/unit/test_
mentoring
.py
→
problem_builder/tests/unit/test_
problem_builder
.py
View file @
318d8c04
File moved
problem_builder/tests/unit/test_step_builder.py
0 → 100644
View file @
318d8c04
import
unittest
import
ddt
from
mock
import
Mock
from
problem_builder.mentoring
import
MentoringWithExplicitStepsBlock
from
.utils
import
ScoresTestMixin
,
instantiate_block
@ddt.ddt
class
TestStepBuilder
(
ScoresTestMixin
,
unittest
.
TestCase
):
""" Unit tests for Step Builder (MentoringWithExplicitStepsBlock) """
def
test_scores
(
self
):
"""
Test that scores are emitted correctly.
"""
# Submit an empty block - score should be 0:
block
=
instantiate_block
(
MentoringWithExplicitStepsBlock
)
with
self
.
expect_score_event
(
block
,
score
=
0.0
,
max_score
=
1.0
):
request
=
Mock
(
method
=
"POST"
,
body
=
"{}"
)
block
.
publish_attempt
(
request
,
suffix
=
None
)
# Mock a block to contain an MCQ question, then submit it. Score should be 1:
block
=
instantiate_block
(
MentoringWithExplicitStepsBlock
)
block
.
questions
=
[
Mock
(
weight
=
1.0
)]
block
.
questions
[
0
]
.
name
=
'mcq1'
block
.
steps
=
[
Mock
(
student_results
=
[(
'mcq1'
,
{
'score'
:
1
,
'status'
:
'correct'
})]
)]
block
.
answer_mapper
=
lambda
_status
:
None
with
self
.
expect_score_event
(
block
,
score
=
1.0
,
max_score
=
1.0
):
request
=
Mock
(
method
=
"POST"
,
body
=
"{}"
)
block
.
publish_attempt
(
request
,
suffix
=
None
)
problem_builder/tests/unit/utils.py
0 → 100644
View file @
318d8c04
"""
Helper methods for testing Problem Builder / Step Builder blocks
"""
from
contextlib
import
contextmanager
from
mock
import
MagicMock
,
Mock
,
patch
from
xblock.field_data
import
DictFieldData
class
ScoresTestMixin
(
object
):
"""
Mixin for tests that involve scores (grades)
"""
def
assert_produces_scores
(
self
,
block
):
"""
Test that the given XBlock instance meets the requirements of being able to report
scores to the edX LMS, and have them appear on the student's progress page.
"""
self
.
assertTrue
(
block
.
has_score
)
self
.
assertTrue
(
type
(
block
)
.
has_score
)
self
.
assertEqual
(
block
.
weight
,
1.0
)
# Default weight should be 1
self
.
assertIsInstance
(
block
.
max_score
(),
(
int
,
float
))
@contextmanager
def
expect_score_event
(
self
,
block
,
score
,
max_score
):
"""
Context manager. Expect that the given block instance will publish the given score.
"""
with
patch
.
object
(
block
.
runtime
,
'publish'
)
as
mocked_publish
:
yield
mocked_publish
.
assert_called_once_with
(
block
,
'grade'
,
{
'value'
:
score
,
'max_value'
:
max_score
})
def
instantiate_block
(
cls
,
fields
=
None
):
"""
Instantiate the given XBlock in a mock runtime.
"""
fields
=
fields
or
{}
children
=
fields
.
pop
(
'children'
,
{})
field_data
=
DictFieldData
(
fields
or
{})
block
=
cls
(
runtime
=
Mock
(),
field_data
=
field_data
,
scope_ids
=
MagicMock
()
)
block
.
children
=
children
block
.
runtime
.
get_block
=
lambda
child_id
:
children
[
child_id
]
return
block
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