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
d969f486
Commit
d969f486
authored
Feb 18, 2016
by
Nimisha Asthagiri
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Course Blocks API: Support accessing a substructure MA-1604
parent
080b7d7b
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
91 additions
and
30 deletions
+91
-30
lms/djangoapps/course_api/blocks/api.py
+1
-1
lms/djangoapps/course_api/blocks/tests/test_api.py
+19
-0
lms/djangoapps/course_api/blocks/views.py
+2
-1
lms/djangoapps/course_blocks/api.py
+10
-22
openedx/core/lib/block_structure/block_structure.py
+15
-0
openedx/core/lib/block_structure/exceptions.py
+8
-1
openedx/core/lib/block_structure/manager.py
+22
-5
openedx/core/lib/block_structure/tests/test_manager.py
+14
-0
No files found.
lms/djangoapps/course_api/blocks/api.py
View file @
d969f486
...
@@ -26,7 +26,7 @@ def get_blocks(
...
@@ -26,7 +26,7 @@ def get_blocks(
Arguments:
Arguments:
request (HTTPRequest): Used for calling django reverse.
request (HTTPRequest): Used for calling django reverse.
usage_key (UsageKey): Identifies the
root
block of interest.
usage_key (UsageKey): Identifies the
starting
block of interest.
user (User): Optional user object for whom the blocks are being
user (User): Optional user object for whom the blocks are being
retrieved. If None, blocks are returned regardless of access checks.
retrieved. If None, blocks are returned regardless of access checks.
depth (integer or None): Identifies the depth of the tree to return
depth (integer or None): Identifies the depth of the tree to return
...
...
lms/djangoapps/course_api/blocks/tests/test_api.py
View file @
d969f486
...
@@ -58,3 +58,22 @@ class TestGetBlocks(EnableTransformerRegistryMixin, SharedModuleStoreTestCase):
...
@@ -58,3 +58,22 @@ class TestGetBlocks(EnableTransformerRegistryMixin, SharedModuleStoreTestCase):
self
.
assertIn
(
unicode
(
problem_block
.
location
),
vertical_descendants
)
self
.
assertIn
(
unicode
(
problem_block
.
location
),
vertical_descendants
)
self
.
assertNotIn
(
unicode
(
self
.
html_block
.
location
),
vertical_descendants
)
self
.
assertNotIn
(
unicode
(
self
.
html_block
.
location
),
vertical_descendants
)
def
test_sub_structure
(
self
):
sequential_block
=
self
.
store
.
get_item
(
self
.
course
.
id
.
make_usage_key
(
'sequential'
,
'sequential_y1'
))
blocks
=
get_blocks
(
self
.
request
,
sequential_block
.
location
,
self
.
user
)
self
.
assertEquals
(
blocks
[
'root'
],
unicode
(
sequential_block
.
location
))
self
.
assertEquals
(
len
(
blocks
[
'blocks'
]),
5
)
for
block_type
,
block_name
,
is_inside_of_structure
in
(
(
'vertical'
,
'vertical_y1a'
,
True
),
(
'problem'
,
'problem_y1a_1'
,
True
),
(
'chapter'
,
'chapter_y'
,
False
),
(
'sequential'
,
'sequential_x1'
,
False
),
):
block
=
self
.
store
.
get_item
(
self
.
course
.
id
.
make_usage_key
(
block_type
,
block_name
))
if
is_inside_of_structure
:
self
.
assertIn
(
unicode
(
block
.
location
),
blocks
[
'blocks'
])
else
:
self
.
assertNotIn
(
unicode
(
block
.
location
),
blocks
[
'blocks'
])
lms/djangoapps/course_api/blocks/views.py
View file @
d969f486
...
@@ -89,7 +89,8 @@ class BlocksView(DeveloperErrorViewMixin, ListAPIView):
...
@@ -89,7 +89,8 @@ class BlocksView(DeveloperErrorViewMixin, ListAPIView):
The following fields are returned with a successful response.
The following fields are returned with a successful response.
* root: The ID of the root node of the course blocks.
* root: The ID of the root node of the requested course block
structure.
* blocks: A dictionary that maps block usage IDs to a collection of
* blocks: A dictionary that maps block usage IDs to a collection of
information about each block. Each block contains the following
information about each block. Each block contains the following
...
...
lms/djangoapps/course_blocks/api.py
View file @
d969f486
...
@@ -28,27 +28,20 @@ COURSE_BLOCK_ACCESS_TRANSFORMERS = [
...
@@ -28,27 +28,20 @@ COURSE_BLOCK_ACCESS_TRANSFORMERS = [
def
get_course_blocks
(
def
get_course_blocks
(
user
,
user
,
root
_block_usage_key
,
starting
_block_usage_key
,
transformers
=
None
,
transformers
=
None
,
):
):
"""
"""
A higher order function implemented on top of the
A higher order function implemented on top of the
block_structure.get_blocks function returning a transformed block
block_structure.get_blocks function returning a transformed block
structure for the given user starting at root_block_usage_key.
structure for the given user starting at starting_block_usage_key.
Note: The current implementation requires the root_block_usage_key
to be the root block of its corresponding course. However, this
is a short-term limitation, which will be addressed in a coming
ticket (https://openedx.atlassian.net/browse/MA-1604). Once that
ticket is implemented, callers will be able to get course blocks
starting at any arbitrary location within a block structure.
Arguments:
Arguments:
user (django.contrib.auth.models.User) - User object for
user (django.contrib.auth.models.User) - User object for
which the block structure is to be transformed.
which the block structure is to be transformed.
root_block_usage_key (UsageKey) - The usage_key for the root
starting_block_usage_key (UsageKey) - Specifies the starting block
of the block structure that is
being access
ed.
of the block structure that is
to be transform
ed.
transformers (BlockStructureTransformers) - A collection of
transformers (BlockStructureTransformers) - A collection of
transformers whose transform methods are to be called.
transformers whose transform methods are to be called.
...
@@ -56,26 +49,21 @@ def get_course_blocks(
...
@@ -56,26 +49,21 @@ def get_course_blocks(
Returns:
Returns:
BlockStructureBlockData - A transformed block structure,
BlockStructureBlockData - A transformed block structure,
starting at
root
_block_usage_key, that has undergone the
starting at
starting
_block_usage_key, that has undergone the
transform methods for the given user and the course
transform methods for the given user and the course
associated with the block structure. If using the default
associated with the block structure. If using the default
transformers, the transformed block structure will be
transformers, the transformed block structure will be
exactly equivalent to the blocks that the given user has
exactly equivalent to the blocks that the given user has
access.
access.
"""
"""
if
root_block_usage_key
!=
modulestore
()
.
make_course_usage_key
(
root_block_usage_key
.
course_key
):
# Enforce this check for now until MA-1604 is implemented.
# Otherwise, callers will get incorrect block data after a
# new version of the course is published, since
# clear_course_from_cache only clears the cached block
# structures starting at the root block of the course.
raise
NotImplementedError
if
not
transformers
:
if
not
transformers
:
transformers
=
BlockStructureTransformers
(
COURSE_BLOCK_ACCESS_TRANSFORMERS
)
transformers
=
BlockStructureTransformers
(
COURSE_BLOCK_ACCESS_TRANSFORMERS
)
transformers
.
usage_info
=
CourseUsageInfo
(
root
_block_usage_key
.
course_key
,
user
)
transformers
.
usage_info
=
CourseUsageInfo
(
starting
_block_usage_key
.
course_key
,
user
)
return
_get_block_structure_manager
(
root_block_usage_key
.
course_key
)
.
get_transformed
(
transformers
)
return
_get_block_structure_manager
(
starting_block_usage_key
.
course_key
)
.
get_transformed
(
transformers
,
starting_block_usage_key
,
)
def
get_course_in_cache
(
course_key
):
def
get_course_in_cache
(
course_key
):
...
...
openedx/core/lib/block_structure/block_structure.py
View file @
d969f486
...
@@ -102,6 +102,21 @@ class BlockStructure(object):
...
@@ -102,6 +102,21 @@ class BlockStructure(object):
"""
"""
return
self
.
_block_relations
[
usage_key
]
.
children
if
self
.
has_block
(
usage_key
)
else
[]
return
self
.
_block_relations
[
usage_key
]
.
children
if
self
.
has_block
(
usage_key
)
else
[]
def
set_root_block
(
self
,
usage_key
):
"""
Sets the given usage key as the new root of the block structure.
Note: This method does *not* prune the rest of the structure. For
performance reasons, it is left to the caller to decide when exactly
to prune.
Arguments:
usage_key - The usage key of the block that is to be set as the
new root of the block structure.
"""
self
.
root_block_usage_key
=
usage_key
self
.
_block_relations
[
usage_key
]
.
parents
=
[]
def
has_block
(
self
,
usage_key
):
def
has_block
(
self
,
usage_key
):
"""
"""
Returns whether a block with the given usage_key is in this
Returns whether a block with the given usage_key is in this
...
...
openedx/core/lib/block_structure/exceptions.py
View file @
d969f486
"""
"""
Application-specific exceptions raised by the block
cach
e framework.
Application-specific exceptions raised by the block
structur
e framework.
"""
"""
...
@@ -8,3 +8,10 @@ class TransformerException(Exception):
...
@@ -8,3 +8,10 @@ class TransformerException(Exception):
Exception class for Transformer related errors.
Exception class for Transformer related errors.
"""
"""
pass
pass
class
UsageKeyNotInBlockStructure
(
Exception
):
"""
Exception for when a usage key is not found within a block structure.
"""
pass
openedx/core/lib/block_structure/manager.py
View file @
d969f486
...
@@ -2,8 +2,9 @@
...
@@ -2,8 +2,9 @@
Top-level module for the Block Structure framework with a class for managing
Top-level module for the Block Structure framework with a class for managing
BlockStructures.
BlockStructures.
"""
"""
from
.factory
import
BlockStructureFactory
from
.cache
import
BlockStructureCache
from
.cache
import
BlockStructureCache
from
.factory
import
BlockStructureFactory
from
.exceptions
import
UsageKeyNotInBlockStructure
from
.transformers
import
BlockStructureTransformers
from
.transformers
import
BlockStructureTransformers
...
@@ -30,23 +31,39 @@ class BlockStructureManager(object):
...
@@ -30,23 +31,39 @@ class BlockStructureManager(object):
self
.
modulestore
=
modulestore
self
.
modulestore
=
modulestore
self
.
block_structure_cache
=
BlockStructureCache
(
cache
)
self
.
block_structure_cache
=
BlockStructureCache
(
cache
)
def
get_transformed
(
self
,
transformers
):
def
get_transformed
(
self
,
transformers
,
starting_block_usage_key
=
None
):
"""
"""
Returns the transformed Block Structure for the root_block_usage_key,
Returns the transformed Block Structure for the root_block_usage_key,
getting block data from the cache and modulestore, as needed.
starting at starting_block_usage_key, getting block data from the cache
and modulestore, as needed.
Details: S
ame as
the get_collected method, except the transformers'
Details: S
imilar to
the get_collected method, except the transformers'
transform methods are also called.
transform methods are also called.
Arguments:
Arguments:
transformers (BlockStructureTransformers) - Collection of
transformers (BlockStructureTransformers) - Collection of
transformers to apply.
transformers to apply.
starting_block_usage_key (UsageKey) - Specifies the starting block
in the block structure that is to be transformed.
If None, root_block_usage_key is used.
Returns:
Returns:
BlockStructureBlockData - A transformed block structure,
BlockStructureBlockData - A transformed block structure,
starting at s
elf.root
_block_usage_key.
starting at s
tarting
_block_usage_key.
"""
"""
block_structure
=
self
.
get_collected
()
block_structure
=
self
.
get_collected
()
if
starting_block_usage_key
:
# Override the root_block_usage_key so traversals start at the
# requested location. The rest of the structure will be pruned
# as part of the transformation.
if
not
block_structure
.
has_block
(
starting_block_usage_key
):
raise
UsageKeyNotInBlockStructure
(
"The requested usage_key '{0}' is not found in the block_structure with root '{1}'"
,
unicode
(
starting_block_usage_key
),
unicode
(
self
.
root_block_usage_key
),
)
block_structure
.
set_root_block
(
starting_block_usage_key
)
transformers
.
transform
(
block_structure
)
transformers
.
transform
(
block_structure
)
return
block_structure
return
block_structure
...
...
openedx/core/lib/block_structure/tests/test_manager.py
View file @
d969f486
...
@@ -3,6 +3,7 @@ Tests for manager.py
...
@@ -3,6 +3,7 @@ Tests for manager.py
"""
"""
from
unittest
import
TestCase
from
unittest
import
TestCase
from
..exceptions
import
UsageKeyNotInBlockStructure
from
..manager
import
BlockStructureManager
from
..manager
import
BlockStructureManager
from
..transformers
import
BlockStructureTransformers
from
..transformers
import
BlockStructureTransformers
from
.helpers
import
(
from
.helpers
import
(
...
@@ -127,6 +128,19 @@ class TestBlockStructureManager(TestCase, ChildrenMapTestMixin):
...
@@ -127,6 +128,19 @@ class TestBlockStructureManager(TestCase, ChildrenMapTestMixin):
TestTransformer1
.
assert_collected
(
block_structure
)
TestTransformer1
.
assert_collected
(
block_structure
)
TestTransformer1
.
assert_transformed
(
block_structure
)
TestTransformer1
.
assert_transformed
(
block_structure
)
def
test_get_transformed_with_starting_block
(
self
):
with
mock_registered_transformers
(
self
.
registered_transformers
):
block_structure
=
self
.
bs_manager
.
get_transformed
(
self
.
transformers
,
starting_block_usage_key
=
1
)
substructure_of_children_map
=
[[],
[
3
,
4
],
[],
[],
[]]
self
.
assert_block_structure
(
block_structure
,
substructure_of_children_map
,
missing_blocks
=
[
0
,
2
])
TestTransformer1
.
assert_collected
(
block_structure
)
TestTransformer1
.
assert_transformed
(
block_structure
)
def
test_get_transformed_with_nonexistent_starting_block
(
self
):
with
mock_registered_transformers
(
self
.
registered_transformers
):
with
self
.
assertRaises
(
UsageKeyNotInBlockStructure
):
self
.
bs_manager
.
get_transformed
(
self
.
transformers
,
starting_block_usage_key
=
100
)
def
test_get_collected_cached
(
self
):
def
test_get_collected_cached
(
self
):
self
.
collect_and_verify
(
expect_modulestore_called
=
True
,
expect_cache_updated
=
True
)
self
.
collect_and_verify
(
expect_modulestore_called
=
True
,
expect_cache_updated
=
True
)
self
.
collect_and_verify
(
expect_modulestore_called
=
False
,
expect_cache_updated
=
False
)
self
.
collect_and_verify
(
expect_modulestore_called
=
False
,
expect_cache_updated
=
False
)
...
...
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