Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
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
edx
edx-platform
Commits
8b5ebb75
Commit
8b5ebb75
authored
Sep 30, 2015
by
Nimisha Asthagiri
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fixup! course_api Navigation support.
parent
9726f070
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
423 additions
and
23 deletions
+423
-23
lms/djangoapps/course_api/blocks/forms.py
+9
-5
lms/djangoapps/course_api/blocks/serializers.py
+1
-2
lms/djangoapps/course_api/blocks/tests/test_forms.py
+20
-3
lms/djangoapps/course_api/blocks/tests/test_views.py
+7
-1
lms/djangoapps/course_api/blocks/transformers/__init__.py
+17
-2
lms/djangoapps/course_api/blocks/transformers/block_depth.py
+45
-0
lms/djangoapps/course_api/blocks/transformers/blocks_api.py
+9
-1
lms/djangoapps/course_api/blocks/transformers/navigation.py
+78
-0
lms/djangoapps/course_api/blocks/transformers/student_view.py
+3
-0
lms/djangoapps/course_api/blocks/transformers/tests/test_block_counts.py
+43
-0
lms/djangoapps/course_api/blocks/transformers/tests/test_block_depth.py
+37
-0
lms/djangoapps/course_api/blocks/transformers/tests/test_navigation.py
+116
-0
lms/djangoapps/course_api/blocks/transformers/tests/test_student_view.py
+34
-5
lms/djangoapps/course_api/blocks/views.py
+4
-4
No files found.
lms/djangoapps/course_api/blocks/forms.py
View file @
8b5ebb75
...
@@ -3,7 +3,7 @@
...
@@ -3,7 +3,7 @@
"""
"""
from
django.contrib.auth.models
import
User
from
django.contrib.auth.models
import
User
from
django.core.exceptions
import
ValidationError
from
django.core.exceptions
import
ValidationError
from
django.forms
import
Form
,
CharField
,
ChoiceField
,
Field
,
MultipleHiddenInput
from
django.forms
import
Form
,
CharField
,
ChoiceField
,
Field
,
IntegerField
,
MultipleHiddenInput
from
django.http
import
Http404
from
django.http
import
Http404
from
rest_framework.exceptions
import
PermissionDenied
from
rest_framework.exceptions
import
PermissionDenied
...
@@ -11,8 +11,6 @@ from xmodule.modulestore.django import modulestore
...
@@ -11,8 +11,6 @@ from xmodule.modulestore.django import modulestore
from
opaque_keys
import
InvalidKeyError
from
opaque_keys
import
InvalidKeyError
from
opaque_keys.edx.keys
import
UsageKey
from
opaque_keys.edx.keys
import
UsageKey
from
transformers.student_view
import
StudentViewTransformer
from
transformers.block_counts
import
BlockCountsTransformer
from
.permissions
import
can_access_other_users_blocks
,
can_access_users_blocks
from
.permissions
import
can_access_other_users_blocks
,
can_access_users_blocks
...
@@ -33,6 +31,7 @@ class BlockListGetForm(Form):
...
@@ -33,6 +31,7 @@ class BlockListGetForm(Form):
student_view_data
=
ListField
(
required
=
False
)
student_view_data
=
ListField
(
required
=
False
)
block_counts
=
ListField
(
required
=
False
)
block_counts
=
ListField
(
required
=
False
)
depth
=
CharField
(
required
=
False
)
depth
=
CharField
(
required
=
False
)
nav_depth
=
IntegerField
(
required
=
False
,
min_value
=
0
)
return_type
=
ChoiceField
(
return_type
=
ChoiceField
(
required
=
False
,
required
=
False
,
choices
=
[(
choice
,
choice
)
for
choice
in
[
'dict'
,
'list'
]],
choices
=
[(
choice
,
choice
)
for
choice
in
[
'dict'
,
'list'
]],
...
@@ -107,8 +106,13 @@ class BlockListGetForm(Form):
...
@@ -107,8 +106,13 @@ class BlockListGetForm(Form):
cleaned_data
=
super
(
BlockListGetForm
,
self
)
.
clean
()
cleaned_data
=
super
(
BlockListGetForm
,
self
)
.
clean
()
# add additional requested_fields that are specified as separate parameters, if they were requested
# add additional requested_fields that are specified as separate parameters, if they were requested
for
additional_field
in
[
StudentViewTransformer
.
STUDENT_VIEW_DATA
,
BlockCountsTransformer
.
BLOCK_COUNTS
]:
additional_requested_fields
=
[
if
cleaned_data
.
get
(
additional_field
):
'student_view_data'
,
'block_counts'
,
'nav_depth'
,
]
for
additional_field
in
additional_requested_fields
:
if
not
cleaned_data
.
get
(
additional_field
)
in
(
None
,
[],
{}):
# allow 0 as a requested value
cleaned_data
[
'requested_fields'
]
.
add
(
additional_field
)
cleaned_data
[
'requested_fields'
]
.
add
(
additional_field
)
usage_key
=
cleaned_data
.
get
(
'usage_key'
)
usage_key
=
cleaned_data
.
get
(
'usage_key'
)
...
...
lms/djangoapps/course_api/blocks/serializers.py
View file @
8b5ebb75
...
@@ -7,7 +7,6 @@ from rest_framework.reverse import reverse
...
@@ -7,7 +7,6 @@ from rest_framework.reverse import reverse
from
transformers
import
SUPPORTED_FIELDS
from
transformers
import
SUPPORTED_FIELDS
# TODO support depth parameter (MA-1366)
class
BlockSerializer
(
serializers
.
Serializer
):
class
BlockSerializer
(
serializers
.
Serializer
):
"""
"""
Serializer for single course block
Serializer for single course block
...
@@ -48,7 +47,7 @@ class BlockSerializer(serializers.Serializer):
...
@@ -48,7 +47,7 @@ class BlockSerializer(serializers.Serializer):
supported_field
.
default_value
,
supported_field
.
default_value
,
)
)
if
field_value
is
not
None
:
if
field_value
is
not
None
:
data
[
supported_field
.
requested
_field_name
]
=
field_value
data
[
supported_field
.
serializer
_field_name
]
=
field_value
if
'children'
in
self
.
context
[
'requested_fields'
]:
if
'children'
in
self
.
context
[
'requested_fields'
]:
children
=
self
.
context
[
'block_structure'
]
.
get_children
(
block_key
)
children
=
self
.
context
[
'block_structure'
]
.
get_children
(
block_key
)
...
...
lms/djangoapps/course_api/blocks/tests/test_forms.py
View file @
8b5ebb75
...
@@ -51,6 +51,7 @@ class TestBlockListGetForm(FormTestMixin, SharedModuleStoreTestCase):
...
@@ -51,6 +51,7 @@ class TestBlockListGetForm(FormTestMixin, SharedModuleStoreTestCase):
self
.
cleaned_data
=
{
self
.
cleaned_data
=
{
'block_counts'
:
[],
'block_counts'
:
[],
'depth'
:
0
,
'depth'
:
0
,
'nav_depth'
:
None
,
'return_type'
:
'dict'
,
'return_type'
:
'dict'
,
'requested_fields'
:
{
'display_name'
,
'type'
},
'requested_fields'
:
{
'display_name'
,
'type'
},
'student_view_data'
:
[],
'student_view_data'
:
[],
...
@@ -68,7 +69,7 @@ class TestBlockListGetForm(FormTestMixin, SharedModuleStoreTestCase):
...
@@ -68,7 +69,7 @@ class TestBlockListGetForm(FormTestMixin, SharedModuleStoreTestCase):
def
assert_equals_cleaned_data
(
self
):
def
assert_equals_cleaned_data
(
self
):
form
=
self
.
get_form
(
expected_valid
=
True
)
form
=
self
.
get_form
(
expected_valid
=
True
)
self
.
assertEqual
(
form
.
cleaned_data
,
self
.
cleaned_data
)
self
.
assert
Dict
Equal
(
form
.
cleaned_data
,
self
.
cleaned_data
)
def
test_basic
(
self
):
def
test_basic
(
self
):
self
.
assert_equals_cleaned_data
()
self
.
assert_equals_cleaned_data
()
...
@@ -141,6 +142,22 @@ class TestBlockListGetForm(FormTestMixin, SharedModuleStoreTestCase):
...
@@ -141,6 +142,22 @@ class TestBlockListGetForm(FormTestMixin, SharedModuleStoreTestCase):
self
.
form_data
[
'depth'
]
=
'not_an_integer'
self
.
form_data
[
'depth'
]
=
'not_an_integer'
self
.
assert_error
(
'depth'
,
"'not_an_integer' is not a valid depth value."
)
self
.
assert_error
(
'depth'
,
"'not_an_integer' is not a valid depth value."
)
#-- nav depth
def
test_nav_depth
(
self
):
self
.
form_data
[
'nav_depth'
]
=
3
self
.
cleaned_data
[
'nav_depth'
]
=
3
self
.
cleaned_data
[
'requested_fields'
]
|=
{
'nav_depth'
}
self
.
assert_equals_cleaned_data
()
def
test_nav_depth_invalid
(
self
):
self
.
form_data
[
'nav_depth'
]
=
'not_an_integer'
self
.
assert_error
(
'nav_depth'
,
"Enter a whole number."
)
def
test_nav_depth_negative
(
self
):
self
.
form_data
[
'nav_depth'
]
=
-
1
self
.
assert_error
(
'nav_depth'
,
"Ensure this value is greater than or equal to 0."
)
#-- return_type
#-- return_type
def
test_return_type
(
self
):
def
test_return_type
(
self
):
...
@@ -158,8 +175,8 @@ class TestBlockListGetForm(FormTestMixin, SharedModuleStoreTestCase):
...
@@ -158,8 +175,8 @@ class TestBlockListGetForm(FormTestMixin, SharedModuleStoreTestCase):
#-- requested fields
#-- requested fields
def
test_requested_fields
(
self
):
def
test_requested_fields
(
self
):
self
.
form_data
.
setlist
(
'requested_fields'
,
[
'graded'
,
'some_other_field'
])
self
.
form_data
.
setlist
(
'requested_fields'
,
[
'graded'
,
'
nav_depth'
,
'
some_other_field'
])
self
.
cleaned_data
[
'requested_fields'
]
|=
{
'graded'
,
'some_other_field'
}
self
.
cleaned_data
[
'requested_fields'
]
|=
{
'graded'
,
'
nav_depth'
,
'
some_other_field'
}
self
.
assert_equals_cleaned_data
()
self
.
assert_equals_cleaned_data
()
@ddt.data
(
'block_counts'
,
'student_view_data'
)
@ddt.data
(
'block_counts'
,
'student_view_data'
)
...
...
lms/djangoapps/course_api/blocks/tests/test_views.py
View file @
8b5ebb75
...
@@ -43,7 +43,7 @@ class TestCourseBlocksView(SharedModuleStoreTestCase):
...
@@ -43,7 +43,7 @@ class TestCourseBlocksView(SharedModuleStoreTestCase):
CourseEnrollmentFactory
.
create
(
user
=
self
.
user
,
course_id
=
self
.
course_key
)
CourseEnrollmentFactory
.
create
(
user
=
self
.
user
,
course_id
=
self
.
course_key
)
def
verify_response
(
self
,
expected_status_code
=
200
,
params
=
None
,
url
=
None
):
def
verify_response
(
self
,
expected_status_code
=
200
,
params
=
None
,
url
=
None
):
query_params
=
{
'user'
:
self
.
user
.
username
}
query_params
=
{
'user'
:
self
.
user
.
username
,
'depth'
:
'all'
,
}
if
params
:
if
params
:
query_params
.
update
(
params
)
query_params
.
update
(
params
)
response
=
self
.
client
.
get
(
url
or
self
.
url
,
query_params
)
response
=
self
.
client
.
get
(
url
or
self
.
url
,
query_params
)
...
@@ -127,6 +127,12 @@ class TestCourseBlocksView(SharedModuleStoreTestCase):
...
@@ -127,6 +127,12 @@ class TestCourseBlocksView(SharedModuleStoreTestCase):
for
block_data
in
response
.
data
[
'blocks'
]
.
itervalues
():
for
block_data
in
response
.
data
[
'blocks'
]
.
itervalues
():
self
.
assert_in_iff
(
'student_view_data'
,
block_data
,
block_data
[
'type'
]
==
'video'
)
self
.
assert_in_iff
(
'student_view_data'
,
block_data
,
block_data
[
'type'
]
==
'video'
)
def
test_navigation_param
(
self
):
response
=
self
.
verify_response
(
params
=
{
'nav_depth'
:
10
})
self
.
verify_response_block_dict
(
response
)
for
block_data
in
response
.
data
[
'blocks'
]
.
itervalues
():
self
.
assertIn
(
'descendants'
,
block_data
)
def
test_requested_fields_param
(
self
):
def
test_requested_fields_param
(
self
):
response
=
self
.
verify_response
(
response
=
self
.
verify_response
(
params
=
{
'requested_fields'
:
[
'graded'
,
'format'
,
'student_view_multi_device'
,
'children'
,
'not_a_field'
]}
params
=
{
'requested_fields'
:
[
'graded'
,
'format'
,
'student_view_multi_device'
,
'children'
,
'not_a_field'
]}
...
...
lms/djangoapps/course_api/blocks/transformers/__init__.py
View file @
8b5ebb75
...
@@ -4,13 +4,21 @@ Course API Block Transformers
...
@@ -4,13 +4,21 @@ Course API Block Transformers
from
student_view
import
StudentViewTransformer
from
student_view
import
StudentViewTransformer
from
block_counts
import
BlockCountsTransformer
from
block_counts
import
BlockCountsTransformer
from
navigation
import
BlockNavigationTransformer
class
SupportedFieldType
(
object
):
class
SupportedFieldType
(
object
):
def
__init__
(
self
,
block_field_name
,
transformer
=
None
,
requested_field_name
=
None
,
default_value
=
None
):
def
__init__
(
self
,
block_field_name
,
transformer
=
None
,
requested_field_name
=
None
,
serializer_field_name
=
None
,
default_value
=
None
):
self
.
transformer
=
transformer
self
.
transformer
=
transformer
self
.
block_field_name
=
block_field_name
self
.
block_field_name
=
block_field_name
self
.
requested_field_name
=
requested_field_name
or
block_field_name
self
.
requested_field_name
=
requested_field_name
or
block_field_name
self
.
serializer_field_name
=
serializer_field_name
or
self
.
requested_field_name
self
.
default_value
=
default_value
self
.
default_value
=
default_value
...
@@ -25,4 +33,11 @@ SUPPORTED_FIELDS = (
...
@@ -25,4 +33,11 @@ SUPPORTED_FIELDS = (
# set the block_field_name to None so the entire data for the transformer is serialized
# set the block_field_name to None so the entire data for the transformer is serialized
SupportedFieldType
(
None
,
BlockCountsTransformer
,
BlockCountsTransformer
.
BLOCK_COUNTS
),
SupportedFieldType
(
None
,
BlockCountsTransformer
,
BlockCountsTransformer
.
BLOCK_COUNTS
),
SupportedFieldType
(
BlockNavigationTransformer
.
BLOCK_NAVIGATION
,
BlockNavigationTransformer
,
requested_field_name
=
'nav_depth'
,
serializer_field_name
=
'descendants'
,
)
)
)
lms/djangoapps/course_api/blocks/transformers/block_depth.py
0 → 100644
View file @
8b5ebb75
from
openedx.core.lib.block_cache.transformer
import
BlockStructureTransformer
class
BlockDepthTransformer
(
BlockStructureTransformer
):
"""
...
"""
VERSION
=
1
BLOCK_DEPTH
=
'block_depth'
def
__init__
(
self
,
requested_depth
=
None
):
self
.
requested_depth
=
requested_depth
@classmethod
def
get_block_depth
(
cls
,
block_structure
,
block_key
):
return
block_structure
.
get_transformer_block_data
(
block_key
,
cls
,
cls
.
BLOCK_DEPTH
,
)
def
transform
(
self
,
user_info
,
block_structure
):
"""
Mutates block_structure based on the given user_info.
"""
for
block_key
in
block_structure
.
topological_traversal
():
parents
=
block_structure
.
get_parents
(
block_key
)
if
parents
:
block_depth
=
min
(
self
.
get_block_depth
(
block_structure
,
parent_key
)
for
parent_key
in
parents
)
+
1
else
:
block_depth
=
0
block_structure
.
set_transformer_block_data
(
block_key
,
self
,
self
.
BLOCK_DEPTH
,
block_depth
)
if
self
.
requested_depth
is
not
None
:
block_structure
.
remove_block_if
(
lambda
block_key
:
self
.
get_block_depth
(
block_structure
,
block_key
)
>
self
.
requested_depth
)
lms/djangoapps/course_api/blocks/transformers/blocks_api.py
View file @
8b5ebb75
from
openedx.core.lib.block_cache.transformer
import
BlockStructureTransformer
from
openedx.core.lib.block_cache.transformer
import
BlockStructureTransformer
from
course_api.blocks.transformers.block_counts
import
BlockCountsTransformer
from
course_api.blocks.transformers.block_counts
import
BlockCountsTransformer
from
course_api.blocks.transformers.student_view
import
StudentViewTransformer
from
course_api.blocks.transformers.student_view
import
StudentViewTransformer
from
course_api.blocks.transformers.block_depth
import
BlockDepthTransformer
from
course_api.blocks.transformers.navigation
import
BlockNavigationTransformer
class
BlocksAPITransformer
(
BlockStructureTransformer
):
class
BlocksAPITransformer
(
BlockStructureTransformer
):
...
@@ -11,9 +13,11 @@ class BlocksAPITransformer(BlockStructureTransformer):
...
@@ -11,9 +13,11 @@ class BlocksAPITransformer(BlockStructureTransformer):
STUDENT_VIEW_DATA
=
'student_view_data'
STUDENT_VIEW_DATA
=
'student_view_data'
STUDENT_VIEW_MULTI_DEVICE
=
'student_view_multi_device'
STUDENT_VIEW_MULTI_DEVICE
=
'student_view_multi_device'
def
__init__
(
self
,
block_types_to_count
,
requested_student_view_data
):
def
__init__
(
self
,
block_types_to_count
,
requested_student_view_data
,
depth
=
None
,
nav_depth
=
None
):
self
.
block_types_to_count
=
block_types_to_count
self
.
block_types_to_count
=
block_types_to_count
self
.
requested_student_view_data
=
requested_student_view_data
self
.
requested_student_view_data
=
requested_student_view_data
self
.
depth
=
depth
self
.
nav_depth
=
nav_depth
@classmethod
@classmethod
def
collect
(
cls
,
block_structure
):
def
collect
(
cls
,
block_structure
):
...
@@ -27,6 +31,8 @@ class BlocksAPITransformer(BlockStructureTransformer):
...
@@ -27,6 +31,8 @@ class BlocksAPITransformer(BlockStructureTransformer):
# collect data from containing transformers
# collect data from containing transformers
StudentViewTransformer
.
collect
(
block_structure
)
StudentViewTransformer
.
collect
(
block_structure
)
BlockCountsTransformer
.
collect
(
block_structure
)
BlockCountsTransformer
.
collect
(
block_structure
)
BlockDepthTransformer
.
collect
(
block_structure
)
BlockNavigationTransformer
.
collect
(
block_structure
)
# TODO support olx_data by calling export_to_xml(?)
# TODO support olx_data by calling export_to_xml(?)
...
@@ -36,3 +42,5 @@ class BlocksAPITransformer(BlockStructureTransformer):
...
@@ -36,3 +42,5 @@ class BlocksAPITransformer(BlockStructureTransformer):
"""
"""
StudentViewTransformer
(
self
.
requested_student_view_data
)
.
transform
(
user_info
,
block_structure
)
StudentViewTransformer
(
self
.
requested_student_view_data
)
.
transform
(
user_info
,
block_structure
)
BlockCountsTransformer
(
self
.
block_types_to_count
)
.
transform
(
user_info
,
block_structure
)
BlockCountsTransformer
(
self
.
block_types_to_count
)
.
transform
(
user_info
,
block_structure
)
BlockDepthTransformer
(
self
.
depth
)
.
transform
(
user_info
,
block_structure
)
BlockNavigationTransformer
(
self
.
nav_depth
)
.
transform
(
user_info
,
block_structure
)
lms/djangoapps/course_api/blocks/transformers/navigation.py
0 → 100644
View file @
8b5ebb75
from
itertools
import
chain
from
openedx.core.lib.block_cache.transformer
import
BlockStructureTransformer
from
.block_depth
import
BlockDepthTransformer
class
DescendantList
(
object
):
def
__init__
(
self
):
self
.
items
=
[]
class
BlockNavigationTransformer
(
BlockStructureTransformer
):
"""
Note: This transformer assumes it is preceded by the BlockDepthTransformer in the transform phase.
"""
VERSION
=
1
BLOCK_NAVIGATION
=
'block_nav'
BLOCK_NAVIGATION_FOR_CHILDREN
=
'children_block_nav'
def
__init__
(
self
,
nav_depth
):
self
.
nav_depth
=
nav_depth
@classmethod
def
collect
(
cls
,
block_structure
):
"""
Collects any information that's necessary to execute this transformer's
transform method.
"""
# collect basic xblock fields
block_structure
.
request_xblock_fields
(
'hide_from_toc'
)
def
transform
(
self
,
user_info
,
block_structure
):
"""
Mutates block_structure based on the given user_info.
"""
if
self
.
nav_depth
is
None
:
return
for
block_key
in
block_structure
.
topological_traversal
():
parents
=
block_structure
.
get_parents
(
block_key
)
parents_descendants_list
=
set
()
for
parent_key
in
parents
:
parent_nav
=
block_structure
.
get_transformer_block_data
(
parent_key
,
self
,
self
.
BLOCK_NAVIGATION_FOR_CHILDREN
,
)
if
parent_nav
is
not
None
:
parents_descendants_list
|=
parent_nav
children_descendants_list
=
None
if
(
not
block_structure
.
get_xblock_field
(
block_key
,
'hide_from_toc'
,
False
)
and
(
not
parents
or
any
(
parent_desc_list
is
not
None
for
parent_desc_list
in
parents_descendants_list
)
)):
# add self to parent's descendants
for
parent_desc_list
in
parents_descendants_list
:
if
parent_desc_list
is
not
None
:
parent_desc_list
.
items
.
append
(
block_key
)
if
BlockDepthTransformer
.
get_block_depth
(
block_structure
,
block_key
)
>
self
.
nav_depth
:
children_descendants_list
=
parents_descendants_list
else
:
block_nav_list
=
DescendantList
()
children_descendants_list
=
{
block_nav_list
}
block_structure
.
set_transformer_block_data
(
block_key
,
self
,
self
.
BLOCK_NAVIGATION
,
block_nav_list
.
items
)
block_structure
.
set_transformer_block_data
(
block_key
,
self
,
self
.
BLOCK_NAVIGATION_FOR_CHILDREN
,
children_descendants_list
)
lms/djangoapps/course_api/blocks/transformers/student_view.py
View file @
8b5ebb75
...
@@ -17,6 +17,9 @@ class StudentViewTransformer(BlockStructureTransformer):
...
@@ -17,6 +17,9 @@ class StudentViewTransformer(BlockStructureTransformer):
"""
"""
Collect student_view_multi_device and student_view_data values for each block
Collect student_view_multi_device and student_view_data values for each block
"""
"""
# collect basic xblock fields
block_structure
.
request_xblock_fields
(
'category'
)
for
block_key
in
block_structure
.
topological_traversal
():
for
block_key
in
block_structure
.
topological_traversal
():
block
=
block_structure
.
get_xblock
(
block_key
)
block
=
block_structure
.
get_xblock
(
block_key
)
...
...
lms/djangoapps/course_api/blocks/transformers/tests/test_block_counts.py
0 → 100644
View file @
8b5ebb75
from
openedx.core.lib.block_cache.block_structure
import
BlockStructureFactory
from
xmodule.modulestore.tests.django_utils
import
ModuleStoreTestCase
from
xmodule.modulestore.tests.factories
import
SampleCourseFactory
from
..block_counts
import
BlockCountsTransformer
class
TestBlockCountsTransformer
(
ModuleStoreTestCase
):
def
setUp
(
self
):
super
(
TestBlockCountsTransformer
,
self
)
.
setUp
()
self
.
course_key
=
SampleCourseFactory
.
create
()
.
id
self
.
course_usage_key
=
self
.
store
.
make_course_usage_key
(
self
.
course_key
)
self
.
block_structure
=
BlockStructureFactory
.
create_from_modulestore
(
self
.
course_usage_key
,
self
.
store
)
def
test_transform
(
self
):
# collect phase
BlockCountsTransformer
.
collect
(
self
.
block_structure
)
self
.
block_structure
.
collect_requested_xblock_fields
()
# transform phase
BlockCountsTransformer
([
'problem'
,
'chapter'
])
.
transform
(
user_info
=
None
,
block_structure
=
self
.
block_structure
)
# block_counts
chapter_x_key
=
self
.
course_key
.
make_usage_key
(
'chapter'
,
'chapter_x'
)
block_counts_for_chapter_x
=
self
.
block_structure
.
get_transformer_block_data
(
chapter_x_key
,
BlockCountsTransformer
,
)
block_counts_for_course
=
self
.
block_structure
.
get_transformer_block_data
(
self
.
course_usage_key
,
BlockCountsTransformer
,
)
# verify count of chapters
self
.
assertEquals
(
block_counts_for_course
[
'chapter'
],
2
)
# verify count of problems
self
.
assertEquals
(
block_counts_for_course
[
'problem'
],
6
)
self
.
assertEquals
(
block_counts_for_chapter_x
[
'problem'
],
3
)
# verify other block types are not counted
for
block_type
in
[
'course'
,
'html'
,
'video'
]:
self
.
assertNotIn
(
block_type
,
block_counts_for_course
)
self
.
assertNotIn
(
block_type
,
block_counts_for_chapter_x
)
lms/djangoapps/course_api/blocks/transformers/tests/test_block_depth.py
0 → 100644
View file @
8b5ebb75
"""
Tests for BlockDepthTransformer.
"""
import
ddt
from
unittest
import
TestCase
from
course_api.blocks.transformers.block_depth
import
BlockDepthTransformer
from
openedx.core.lib.block_cache.tests.test_utils
import
ChildrenMapTestMixin
from
openedx.core.lib.block_cache.block_structure
import
BlockStructureCollectedData
@ddt.ddt
class
BlockDepthTransformerTestCase
(
TestCase
,
ChildrenMapTestMixin
):
"""
...
"""
@ddt.data
(
(
0
,
[],
[],
[]),
(
0
,
ChildrenMapTestMixin
.
SIMPLE_CHILDREN_MAP
,
[[],
[],
[],
[],
[]],
[
1
,
2
,
3
,
4
]),
(
1
,
ChildrenMapTestMixin
.
SIMPLE_CHILDREN_MAP
,
[[
1
,
2
],
[],
[],
[],
[]],
[
3
,
4
]),
(
2
,
ChildrenMapTestMixin
.
SIMPLE_CHILDREN_MAP
,
ChildrenMapTestMixin
.
SIMPLE_CHILDREN_MAP
,
[]),
(
3
,
ChildrenMapTestMixin
.
SIMPLE_CHILDREN_MAP
,
ChildrenMapTestMixin
.
SIMPLE_CHILDREN_MAP
,
[]),
(
None
,
ChildrenMapTestMixin
.
SIMPLE_CHILDREN_MAP
,
ChildrenMapTestMixin
.
SIMPLE_CHILDREN_MAP
,
[]),
(
0
,
ChildrenMapTestMixin
.
DAG_CHILDREN_MAP
,
[[],
[],
[],
[],
[],
[],
[]],
[
1
,
2
,
3
,
4
,
5
,
6
]),
(
1
,
ChildrenMapTestMixin
.
DAG_CHILDREN_MAP
,
[[
1
,
2
],
[],
[],
[],
[],
[],
[]],
[
3
,
4
,
5
,
6
]),
(
2
,
ChildrenMapTestMixin
.
DAG_CHILDREN_MAP
,
[[
1
,
2
],
[
3
],
[
3
,
4
],
[],
[],
[],
[]],
[
5
,
6
]),
(
3
,
ChildrenMapTestMixin
.
DAG_CHILDREN_MAP
,
ChildrenMapTestMixin
.
DAG_CHILDREN_MAP
,
[]),
(
4
,
ChildrenMapTestMixin
.
DAG_CHILDREN_MAP
,
ChildrenMapTestMixin
.
DAG_CHILDREN_MAP
,
[]),
(
None
,
ChildrenMapTestMixin
.
DAG_CHILDREN_MAP
,
ChildrenMapTestMixin
.
DAG_CHILDREN_MAP
,
[]),
)
@ddt.unpack
def
test_block_depth
(
self
,
block_depth
,
children_map
,
transformed_children_map
,
missing_blocks
):
block_structure
=
self
.
create_block_structure
(
BlockStructureCollectedData
,
children_map
)
BlockDepthTransformer
(
block_depth
)
.
transform
(
user_info
=
None
,
block_structure
=
block_structure
)
block_structure
.
prune
()
self
.
assert_block_structure
(
block_structure
,
transformed_children_map
,
missing_blocks
)
lms/djangoapps/course_api/blocks/transformers/tests/test_navigation.py
0 → 100644
View file @
8b5ebb75
"""
Tests for BlockNavigationTransformer.
"""
import
ddt
from
unittest
import
TestCase
from
course_api.blocks.transformers.block_depth
import
BlockDepthTransformer
from
course_api.blocks.transformers.navigation
import
BlockNavigationTransformer
from
openedx.core.lib.block_cache.tests.test_utils
import
ChildrenMapTestMixin
from
openedx.core.lib.block_cache.block_structure
import
BlockStructureCollectedData
,
BlockStructureFactory
from
xmodule.modulestore.tests.django_utils
import
ModuleStoreTestCase
from
xmodule.modulestore.tests.factories
import
SampleCourseFactory
from
xmodule.modulestore
import
ModuleStoreEnum
@ddt.ddt
class
BlockNavigationTransformerTestCase
(
TestCase
,
ChildrenMapTestMixin
):
"""
Course-agnostic test class for testing the Navigation transformer.
"""
@ddt.data
(
(
0
,
0
,
[],
[]),
(
0
,
0
,
ChildrenMapTestMixin
.
LINEAR_CHILDREN_MAP
,
[[],
[],
[],
[]]),
(
None
,
0
,
ChildrenMapTestMixin
.
LINEAR_CHILDREN_MAP
,
[[
1
,
2
,
3
],
[],
[],
[]]),
(
None
,
1
,
ChildrenMapTestMixin
.
LINEAR_CHILDREN_MAP
,
[[
1
],
[
2
,
3
],
[],
[]]),
(
None
,
2
,
ChildrenMapTestMixin
.
LINEAR_CHILDREN_MAP
,
[[
1
],
[
2
],
[
3
],
[]]),
(
None
,
3
,
ChildrenMapTestMixin
.
LINEAR_CHILDREN_MAP
,
[[
1
],
[
2
],
[
3
],
[]]),
(
None
,
4
,
ChildrenMapTestMixin
.
LINEAR_CHILDREN_MAP
,
[[
1
],
[
2
],
[
3
],
[]]),
(
1
,
4
,
ChildrenMapTestMixin
.
LINEAR_CHILDREN_MAP
,
[[
1
],
[],
[],
[]]),
(
2
,
4
,
ChildrenMapTestMixin
.
LINEAR_CHILDREN_MAP
,
[[
1
],
[
2
],
[],
[]]),
(
0
,
0
,
ChildrenMapTestMixin
.
DAG_CHILDREN_MAP
,
[[],
[],
[],
[],
[],
[],
[]]),
(
0
,
0
,
ChildrenMapTestMixin
.
DAG_CHILDREN_MAP
,
[[],
[],
[],
[],
[],
[],
[]]),
(
None
,
0
,
ChildrenMapTestMixin
.
DAG_CHILDREN_MAP
,
[[
1
,
2
,
3
,
4
,
5
,
6
],
[],
[],
[],
[],
[],
[]]),
(
None
,
1
,
ChildrenMapTestMixin
.
DAG_CHILDREN_MAP
,
[[
1
,
2
],
[
3
,
5
,
6
],
[
3
,
4
,
5
,
6
],
[],
[],
[],
[]]),
(
None
,
2
,
ChildrenMapTestMixin
.
DAG_CHILDREN_MAP
,
[[
1
,
2
],
[
3
],
[
3
,
4
],
[
5
,
6
],
[],
[],
[]]),
(
None
,
3
,
ChildrenMapTestMixin
.
DAG_CHILDREN_MAP
,
[[
1
,
2
],
[
3
],
[
3
,
4
],
[
5
,
6
],
[],
[],
[]]),
(
None
,
4
,
ChildrenMapTestMixin
.
DAG_CHILDREN_MAP
,
[[
1
,
2
],
[
3
],
[
3
,
4
],
[
5
,
6
],
[],
[],
[]]),
(
1
,
4
,
ChildrenMapTestMixin
.
DAG_CHILDREN_MAP
,
[[
1
,
2
],
[],
[],
[],
[],
[],
[]]),
(
2
,
4
,
ChildrenMapTestMixin
.
DAG_CHILDREN_MAP
,
[[
1
,
2
],
[
3
],
[
3
,
4
],
[],
[],
[],
[]]),
)
@ddt.unpack
def
test_navigation
(
self
,
depth
,
nav_depth
,
children_map
,
expected_nav_map
):
block_structure
=
self
.
create_block_structure
(
BlockStructureCollectedData
,
children_map
)
BlockDepthTransformer
(
depth
)
.
transform
(
user_info
=
None
,
block_structure
=
block_structure
)
BlockNavigationTransformer
(
nav_depth
)
.
transform
(
user_info
=
None
,
block_structure
=
block_structure
)
block_structure
.
prune
()
for
block_key
,
expected_nav
in
enumerate
(
expected_nav_map
):
self
.
assertSetEqual
(
set
(
expected_nav
),
set
(
block_structure
.
get_transformer_block_data
(
block_key
,
BlockNavigationTransformer
,
BlockNavigationTransformer
.
BLOCK_NAVIGATION
,
[]
)),
)
class
BlockNavigationTransformerCourseTestCase
(
ModuleStoreTestCase
):
"""
Uses SampleCourseFactory and Modulestore to test the Navigation transformer.
"""
def
test_hide_from_toc
(
self
):
course_key
=
SampleCourseFactory
.
create
()
.
id
course_usage_key
=
self
.
store
.
make_course_usage_key
(
course_key
)
# hide chapter_x from TOC
chapter_x_key
=
course_key
.
make_usage_key
(
'chapter'
,
'chapter_x'
)
chapter_x
=
self
.
store
.
get_item
(
chapter_x_key
)
chapter_x
.
hide_from_toc
=
True
self
.
store
.
update_item
(
chapter_x
,
ModuleStoreEnum
.
UserID
.
test
)
block_structure
=
BlockStructureFactory
.
create_from_modulestore
(
course_usage_key
,
self
.
store
)
# collect phase
BlockDepthTransformer
.
collect
(
block_structure
)
BlockNavigationTransformer
.
collect
(
block_structure
)
block_structure
.
collect_requested_xblock_fields
()
self
.
assertTrue
(
block_structure
.
has_block
(
chapter_x_key
))
# transform phase
BlockDepthTransformer
()
.
transform
(
user_info
=
None
,
block_structure
=
block_structure
)
BlockNavigationTransformer
(
0
)
.
transform
(
user_info
=
None
,
block_structure
=
block_structure
)
block_structure
.
prune
()
self
.
assertTrue
(
block_structure
.
has_block
(
chapter_x_key
))
course_descendants
=
block_structure
.
get_transformer_block_data
(
course_usage_key
,
BlockNavigationTransformer
,
BlockNavigationTransformer
.
BLOCK_NAVIGATION
,
)
# chapter_y and its descendants should be included
for
block_key
in
[
course_key
.
make_usage_key
(
'chapter'
,
'chapter_y'
),
course_key
.
make_usage_key
(
'sequential'
,
'sequential_y1'
),
course_key
.
make_usage_key
(
'vertical'
,
'vertical_y1a'
),
course_key
.
make_usage_key
(
'problem'
,
'problem_y1a_1'
),
]:
self
.
assertIn
(
block_key
,
course_descendants
)
# chapter_x and its descendants should not be included
for
block_key
in
[
chapter_x_key
,
course_key
.
make_usage_key
(
'sequential'
,
'sequential_x1'
),
course_key
.
make_usage_key
(
'vertical'
,
'vertical_x1a'
),
course_key
.
make_usage_key
(
'problem'
,
'problem_x1a_1'
),
]:
self
.
assertNotIn
(
block_key
,
course_descendants
)
lms/djangoapps/course_api/blocks/transformers/tests/test_student_view.py
View file @
8b5ebb75
...
@@ -10,10 +10,39 @@ class TestStudentViewTransformer(ModuleStoreTestCase):
...
@@ -10,10 +10,39 @@ class TestStudentViewTransformer(ModuleStoreTestCase):
def
setUp
(
self
):
def
setUp
(
self
):
super
(
TestStudentViewTransformer
,
self
)
.
setUp
()
super
(
TestStudentViewTransformer
,
self
)
.
setUp
()
self
.
course_key
=
ToyCourseFactory
.
create
()
.
id
self
.
course_key
=
ToyCourseFactory
.
create
()
.
id
self
.
course_root_loc
=
self
.
store
.
make_course_usage_key
(
self
.
course_key
)
self
.
course_usage_key
=
self
.
store
.
make_course_usage_key
(
self
.
course_key
)
self
.
block_structure
=
BlockStructureFactory
.
create_from_modulestore
(
self
.
block_structure
=
BlockStructureFactory
.
create_from_modulestore
(
self
.
course_usage_key
,
self
.
store
)
self
.
course_root_loc
,
self
.
store
)
def
test_collect
(
self
):
def
test_transform
(
self
):
# collect phase
StudentViewTransformer
.
collect
(
self
.
block_structure
)
StudentViewTransformer
.
collect
(
self
.
block_structure
)
self
.
block_structure
.
collect_requested_xblock_fields
()
# transform phase
StudentViewTransformer
(
'video'
)
.
transform
(
user_info
=
None
,
block_structure
=
self
.
block_structure
)
# verify video data
video_block_key
=
self
.
course_key
.
make_usage_key
(
'video'
,
'sample_video'
)
self
.
assertIsNotNone
(
self
.
block_structure
.
get_transformer_block_data
(
video_block_key
,
StudentViewTransformer
,
StudentViewTransformer
.
STUDENT_VIEW_DATA
,
)
)
self
.
assertFalse
(
self
.
block_structure
.
get_transformer_block_data
(
video_block_key
,
StudentViewTransformer
,
StudentViewTransformer
.
STUDENT_VIEW_MULTI_DEVICE
,
)
)
# verify html data
html_block_key
=
self
.
course_key
.
make_usage_key
(
'html'
,
'toyhtml'
)
self
.
assertIsNone
(
self
.
block_structure
.
get_transformer_block_data
(
html_block_key
,
StudentViewTransformer
,
StudentViewTransformer
.
STUDENT_VIEW_DATA
,
)
)
self
.
assertTrue
(
self
.
block_structure
.
get_transformer_block_data
(
html_block_key
,
StudentViewTransformer
,
StudentViewTransformer
.
STUDENT_VIEW_MULTI_DEVICE
,
)
)
lms/djangoapps/course_api/blocks/views.py
View file @
8b5ebb75
...
@@ -8,8 +8,6 @@ from openedx.core.lib.api.view_utils import view_auth_classes, DeveloperErrorVie
...
@@ -8,8 +8,6 @@ from openedx.core.lib.api.view_utils import view_auth_classes, DeveloperErrorVie
from
xmodule.modulestore.exceptions
import
ItemNotFoundError
from
xmodule.modulestore.exceptions
import
ItemNotFoundError
from
transformers.blocks_api
import
BlocksAPITransformer
from
transformers.blocks_api
import
BlocksAPITransformer
from
transformers.block_counts
import
BlockCountsTransformer
from
transformers.student_view
import
StudentViewTransformer
from
.forms
import
BlockListGetForm
from
.forms
import
BlockListGetForm
from
.serializers
import
BlockSerializer
,
BlockDictSerializer
from
.serializers
import
BlockSerializer
,
BlockDictSerializer
...
@@ -127,8 +125,10 @@ class CourseBlocks(DeveloperErrorViewMixin, ListAPIView):
...
@@ -127,8 +125,10 @@ class CourseBlocks(DeveloperErrorViewMixin, ListAPIView):
# transform blocks
# transform blocks
blocks_api_transformer
=
BlocksAPITransformer
(
blocks_api_transformer
=
BlocksAPITransformer
(
params
.
cleaned_data
.
get
(
BlockCountsTransformer
.
BLOCK_COUNTS
,
[]),
params
.
cleaned_data
.
get
(
'block_counts'
,
[]),
params
.
cleaned_data
.
get
(
StudentViewTransformer
.
STUDENT_VIEW_DATA
,
[]),
params
.
cleaned_data
.
get
(
'student_view_data'
,
[]),
params
.
cleaned_data
.
get
(
'depth'
,
None
),
params
.
cleaned_data
.
get
(
'nav_depth'
,
None
),
)
)
try
:
try
:
blocks
=
get_course_blocks
(
blocks
=
get_course_blocks
(
...
...
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