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
a3a48a98
Commit
a3a48a98
authored
Aug 31, 2015
by
Dino Cikatic
Committed by
Nimisha Asthagiri
Oct 05, 2015
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Block Transformers: Split Test and Content Library
parent
82f3c0df
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
679 additions
and
78 deletions
+679
-78
lms/djangoapps/course_blocks/api.py
+4
-0
lms/djangoapps/course_blocks/transformers/helpers.py
+38
-0
lms/djangoapps/course_blocks/transformers/library_content.py
+103
-0
lms/djangoapps/course_blocks/transformers/split_test.py
+110
-0
lms/djangoapps/course_blocks/transformers/tests/test_helpers.py
+14
-3
lms/djangoapps/course_blocks/transformers/tests/test_library_content.py
+161
-0
lms/djangoapps/course_blocks/transformers/tests/test_split_test.py
+221
-0
lms/djangoapps/course_blocks/transformers/tests/test_user_partitions.py
+11
-16
lms/djangoapps/course_blocks/transformers/user_partitions.py
+15
-59
setup.py
+2
-0
No files found.
lms/djangoapps/course_blocks/api.py
View file @
a3a48a98
...
...
@@ -10,6 +10,8 @@ from .transformers import (
start_date
,
user_partitions
,
visibility
,
split_test
,
library_content
,
)
from
.user_info
import
CourseUserInfo
...
...
@@ -18,6 +20,8 @@ LMS_COURSE_TRANSFORMERS = [
visibility
.
VisibilityTransformer
(),
start_date
.
StartDateTransformer
(),
user_partitions
.
UserPartitionTransformer
(),
split_test
.
SplitTestTransformer
(),
library_content
.
ContentLibraryTransformer
(),
]
...
...
lms/djangoapps/course_blocks/transformers/helpers.py
0 → 100644
View file @
a3a48a98
"""
Transformers helpers functions.
"""
from
openedx.core.djangoapps.user_api.partition_schemes
import
RandomUserPartitionScheme
from
openedx.core.djangoapps.course_groups.partition_scheme
import
CohortPartitionScheme
# TODO 8874: Make it so we support all schemes instead of manually declaring them here.
INCLUDE_SCHEMES
=
[
CohortPartitionScheme
,
RandomUserPartitionScheme
,
]
SCHEME_SUPPORTS_ASSIGNMENT
=
[
RandomUserPartitionScheme
,
]
def
get_user_partition_groups
(
course_key
,
user_partitions
,
user
):
"""
Collect group ID for each partition in this course for this user.
Arguments:
course_key (CourseKey)
user_partitions (list[UserPartition])
user (User)
Returns:
dict[int: Group]: Mapping from user partitions to the group to which
the user belongs in each partition. If the user isn't in a group
for a particular partition, then that partition's ID will not be
in the dict.
"""
partition_groups
=
{}
for
partition
in
user_partitions
:
if
partition
.
scheme
not
in
INCLUDE_SCHEMES
:
continue
group
=
partition
.
scheme
.
get_group_for_user
(
course_key
,
user
,
partition
,
**
({
'assign'
:
False
}
if
partition
.
scheme
in
SCHEME_SUPPORTS_ASSIGNMENT
else
{})
)
if
group
is
not
None
:
partition_groups
[
partition
.
id
]
=
group
return
partition_groups
lms/djangoapps/course_blocks/transformers/library_content.py
0 → 100644
View file @
a3a48a98
"""
Content Library Transformer, used to filter course structure per user.
"""
import
json
from
courseware.access
import
_has_access_to_course
from
courseware.models
import
StudentModule
from
opaque_keys.edx.locator
import
BlockUsageLocator
from
openedx.core.lib.block_cache.transformer
import
BlockStructureTransformer
class
ContentLibraryTransformer
(
BlockStructureTransformer
):
"""
Content Library Transformer Class
"""
VERSION
=
1
@classmethod
def
_get_selected_modules
(
cls
,
user
,
course_key
,
block_key
):
"""
Get list of selected modules in a library,
for user.
Arguments:
user (User)
course_key (CourseLocator)
block_key (BlockUsageLocator)
Returns:
list[modules]
"""
return
StudentModule
.
objects
.
filter
(
student
=
user
,
course_id
=
course_key
,
module_state_key
=
block_key
,
state__contains
=
'"selected": [['
)
@classmethod
def
collect
(
cls
,
block_structure
):
"""
Computes any information for each XBlock that's necessary to execute
this transformer's transform method.
Arguments:
block_structure (BlockStructureCollectedData)
Returns:
dict[UsageKey: dict]
"""
# For each block check if block is library_content.
# If library_content add children array to content_library_children field
for
block_key
in
block_structure
.
topological_traversal
():
xblock
=
block_structure
.
get_xblock
(
block_key
)
block_structure
.
set_transformer_block_data
(
block_key
,
cls
,
'content_library_children'
,
[])
if
getattr
(
xblock
,
'category'
,
None
)
==
'library_content'
:
block_structure
.
set_transformer_block_data
(
block_key
,
cls
,
'content_library_children'
,
xblock
.
children
)
def
transform
(
self
,
user_info
,
block_structure
):
"""
Mutates block_structure and block_data based on the given user_info.
Arguments:
user_info(object)
block_structure (BlockStructureCollectedData)
"""
def
check_child_removal
(
block_key
):
"""
Check if selected block should be removed.
Block is removed if it is part of library_content, but has not been selected
for current user.
"""
if
block_key
not
in
children
:
return
False
if
block_key
in
children
and
block_key
in
selected_children
:
return
False
return
True
children
=
[]
selected_children
=
[]
for
block_key
in
block_structure
.
get_block_keys
():
library_children
=
block_structure
.
get_transformer_block_data
(
block_key
,
self
,
'content_library_children'
)
if
library_children
:
children
.
extend
(
library_children
)
# Retrieve "selected" json from LMS MySQL database.
modules
=
self
.
_get_selected_modules
(
user_info
.
user
,
user_info
.
course_key
,
block_key
)
for
module
in
modules
:
module_state
=
module
.
state
state_dict
=
json
.
loads
(
module_state
)
# Check all selected entries for this user on selected library.
# Add all selected to selected_children list.
for
state
in
state_dict
[
'selected'
]:
usage_key
=
BlockUsageLocator
(
user_info
.
course_key
,
block_type
=
state
[
0
],
block_id
=
state
[
1
]
)
if
usage_key
in
library_children
:
selected_children
.
append
(
usage_key
)
# Check and remove all non-selected children from course structure.
if
not
user_info
.
has_staff_access
:
block_structure
.
remove_block_if
(
check_child_removal
)
lms/djangoapps/course_blocks/transformers/split_test.py
0 → 100644
View file @
a3a48a98
"""
Split Test Block Transformer, used to filter course structure per user.
"""
from
openedx.core.lib.block_cache.transformer
import
BlockStructureTransformer
from
.helpers
import
get_user_partition_groups
class
SplitTestTransformer
(
BlockStructureTransformer
):
"""
Split Test Transformer Class
"""
VERSION
=
1
@staticmethod
def
check_split_access
(
split_test_groups
,
user_groups
):
"""
Check that user has access to specific split test group.
Arguments:
split_test_groups (list)
user_groups (dict[Partition Id: Group])
Returns:
bool
"""
if
split_test_groups
:
for
_
,
group
in
user_groups
.
iteritems
():
if
group
.
id
in
split_test_groups
:
return
True
return
False
return
True
@classmethod
def
collect
(
cls
,
block_structure
):
"""
Computes any information for each XBlock that's necessary to execute
this transformer's transform method.
Arguments:
block_structure (BlockStructureCollectedData)
"""
# Check potential previously set values for user_partitions and split_test_partitions
xblock
=
block_structure
.
get_xblock
(
block_structure
.
root_block_key
)
user_partitions
=
getattr
(
xblock
,
'user_partitions'
,
[])
split_test_partitions
=
getattr
(
xblock
,
'split_test_partition'
,
[])
or
[]
# For each block, check if there is an split_test block.
# If split_test is found, check it's user_partition value and get children.
# Set split_test_group on each of the children for fast retrival in transform phase.
# Add same group to childrens children, because due to structure restrictions first level
# children are verticals.
for
block_key
in
block_structure
.
topological_traversal
():
xblock
=
block_structure
.
get_xblock
(
block_key
)
category
=
getattr
(
xblock
,
'category'
,
None
)
if
category
==
'split_test'
:
for
user_partition
in
user_partitions
:
if
user_partition
.
id
==
xblock
.
user_partition_id
:
if
user_partition
not
in
split_test_partitions
:
split_test_partitions
.
append
(
user_partition
)
for
child
in
xblock
.
children
:
for
group
in
user_partition
.
groups
:
child_location
=
xblock
.
group_id_to_child
.
get
(
unicode
(
group
.
id
),
None
)
if
child_location
==
child
:
block_structure
.
set_transformer_block_data
(
child
,
cls
,
'split_test_groups'
,
[
group
.
id
]
)
for
component
in
block_structure
.
get_xblock
(
child
)
.
children
:
block_structure
.
set_transformer_block_data
(
component
,
cls
,
'split_test_groups'
,
[
group
.
id
]
)
block_structure
.
set_transformer_data
(
cls
,
'split_test_partition'
,
split_test_partitions
)
def
transform
(
self
,
user_info
,
block_structure
):
"""
Mutates block_structure and block_data based on the given user_info.
Arguments:
user_info (object)
block_structure (BlockStructureCollectedData)
"""
user_partitions
=
block_structure
.
get_transformer_data
(
self
,
'split_test_partition'
)
# If there are no split test user partitions, this transformation is a no-op,
# so there is nothing to transform.
if
not
user_partitions
:
return
user_groups
=
get_user_partition_groups
(
user_info
.
course_key
,
user_partitions
,
user_info
.
user
)
if
not
user_info
.
has_staff_access
:
block_structure
.
remove_block_if
(
lambda
block_key
:
not
SplitTestTransformer
.
check_split_access
(
block_structure
.
get_transformer_block_data
(
block_key
,
self
,
'split_test_groups'
,
default
=
[]
),
user_groups
)
)
lms/djangoapps/course_blocks/transformers/tests/test_helpers.py
View file @
a3a48a98
...
...
@@ -15,6 +15,7 @@ class CourseStructureTestCase(ModuleStoreTestCase):
"""
Helper for test cases that need to build course structures.
"""
blocks
=
[]
def
build_course
(
self
,
course_hierarchy
):
"""
...
...
@@ -76,6 +77,16 @@ class CourseStructureTestCase(ModuleStoreTestCase):
return
block_map
def
get_block_key_set
(
self
,
*
refs
):
"""
Gets the set of usage keys that correspond to the list of
#ref values as defined on self.blocks.
Returns: set[UsageKey]
"""
xblocks
=
(
self
.
blocks
[
ref
]
for
ref
in
refs
)
return
set
([
xblock
.
location
for
xblock
in
xblocks
])
class
BlockParentsMapTestCase
(
ModuleStoreTestCase
):
# Tree formed by parent_map:
...
...
@@ -137,7 +148,8 @@ class BlockParentsMapTestCase(ModuleStoreTestCase):
i
in
expected_accessible_blocks
,
"block_structure return value {0} not equal to expected value for block {1}"
.
format
(
block_structure_result
,
i
))
)
)
if
i
in
blocks_with_differing_access
:
self
.
assertNotEqual
(
...
...
@@ -160,4 +172,4 @@ class BlockParentsMapTestCase(ModuleStoreTestCase):
return
modulestore
()
.
get_item
(
self
.
xblock_keys
[
i
])
def
update_block
(
self
,
block
):
return
modulestore
()
.
update_item
(
block
,
'test_user'
)
\ No newline at end of file
return
modulestore
()
.
update_item
(
block
,
'test_user'
)
lms/djangoapps/course_blocks/transformers/tests/test_library_content.py
0 → 100644
View file @
a3a48a98
"""
Tests for ContentLibraryTransformer.
"""
import
mock
from
student.tests.factories
import
UserFactory
from
student.tests.factories
import
CourseEnrollmentFactory
from
course_blocks.transformers.library_content
import
ContentLibraryTransformer
from
course_blocks.api
import
get_course_blocks
,
clear_course_from_cache
from
lms.djangoapps.course_blocks.transformers.tests.test_helpers
import
CourseStructureTestCase
class
MockedModules
(
object
):
"""
Object with mocked selected modules for user.
"""
def
__init__
(
self
,
state
):
"""
Set state attribute on initialize.
"""
self
.
state
=
state
class
ContentLibraryTransformerTestCase
(
CourseStructureTestCase
):
"""
ContentLibraryTransformer Test
"""
def
setUp
(
self
):
"""
Setup course structure and create user for content library transformer test.
"""
super
(
ContentLibraryTransformerTestCase
,
self
)
.
setUp
()
# Build course.
self
.
course_hierarchy
=
self
.
get_test_course_hierarchy
()
self
.
blocks
=
self
.
build_course
(
self
.
course_hierarchy
)
self
.
course
=
self
.
blocks
[
'course'
]
clear_course_from_cache
(
self
.
course
.
id
)
# Set up user and enroll in course.
self
.
password
=
'test'
self
.
user
=
UserFactory
.
create
(
password
=
self
.
password
)
CourseEnrollmentFactory
.
create
(
user
=
self
.
user
,
course_id
=
self
.
course
.
id
,
is_active
=
True
)
self
.
selected_modules
=
[
MockedModules
(
'{"selected": [["vertical", "vertical_vertical2"]]}'
)]
self
.
transformer
=
[]
def
get_test_course_hierarchy
(
self
):
"""
Get a course hierarchy to test with.
"""
return
{
'org'
:
'ContentLibraryTransformer'
,
'course'
:
'CL101F'
,
'run'
:
'test_run'
,
'#ref'
:
'course'
,
'#children'
:
[
{
'#type'
:
'chapter'
,
'#ref'
:
'chapter1'
,
'#children'
:
[
{
'#type'
:
'sequential'
,
'#ref'
:
'lesson1'
,
'#children'
:
[
{
'#type'
:
'vertical'
,
'#ref'
:
'vertical1'
,
'#children'
:
[
{
'metadata'
:
{
'category'
:
'library_content'
},
'#type'
:
'library_content'
,
'#ref'
:
'library_content1'
,
'#children'
:
[
{
'metadata'
:
{
'display_name'
:
"CL Vertical 1"
},
'#type'
:
'vertical'
,
'#ref'
:
'vertical2'
,
'#children'
:
[
{
'metadata'
:
{
'display_name'
:
"HTML1"
},
'#type'
:
'html'
,
'#ref'
:
'html1'
,
}
]
},
{
'metadata'
:
{
'display_name'
:
"CL Vertical 2"
},
'#type'
:
'vertical'
,
'#ref'
:
'vertical3'
,
'#children'
:
[
{
'metadata'
:
{
'display_name'
:
"HTML2"
},
'#type'
:
'html'
,
'#ref'
:
'html2'
,
}
]
}
]
}
],
}
],
}
],
}
]
}
def
test_course_structure_with_user_course_library
(
self
):
"""
Test course structure integrity if course has content library section.
First test user can't see any content library section,
and after that mock response from MySQL db.
Check user can see mocked sections in content library.
"""
self
.
transformer
=
ContentLibraryTransformer
()
raw_block_structure
=
get_course_blocks
(
self
.
user
,
self
.
course
.
location
,
transformers
=
{}
)
self
.
assertEqual
(
len
(
list
(
raw_block_structure
.
get_block_keys
())),
len
(
self
.
blocks
))
clear_course_from_cache
(
self
.
course
.
id
)
trans_block_structure
=
get_course_blocks
(
self
.
user
,
self
.
course
.
location
,
transformers
=
{
self
.
transformer
}
)
self
.
assertEqual
(
set
(
trans_block_structure
.
get_block_keys
()),
self
.
get_block_key_set
(
'course'
,
'chapter1'
,
'lesson1'
,
'vertical1'
,
'library_content1'
)
)
# Check course structure again, with mocked selected modules for a user.
with
mock
.
patch
(
'course_blocks.transformers.library_content.ContentLibraryTransformer._get_selected_modules'
,
return_value
=
self
.
selected_modules
):
clear_course_from_cache
(
self
.
course
.
id
)
trans_block_structure
=
get_course_blocks
(
self
.
user
,
self
.
course
.
location
,
transformers
=
{
self
.
transformer
}
)
self
.
assertEqual
(
set
(
trans_block_structure
.
get_block_keys
()),
self
.
get_block_key_set
(
'course'
,
'chapter1'
,
'lesson1'
,
'vertical1'
,
'library_content1'
,
'vertical2'
,
'html1'
)
)
lms/djangoapps/course_blocks/transformers/tests/test_split_test.py
0 → 100644
View file @
a3a48a98
"""
Tests for SplitTestTransformer.
"""
from
openedx.core.djangoapps.user_api.partition_schemes
import
RandomUserPartitionScheme
from
opaque_keys.edx.keys
import
CourseKey
from
student.tests.factories
import
UserFactory
from
student.tests.factories
import
CourseEnrollmentFactory
from
xmodule.modulestore.django
import
modulestore
from
xmodule.partitions.partitions
import
Group
,
UserPartition
from
course_blocks.transformers.split_test
import
SplitTestTransformer
from
course_blocks.api
import
get_course_blocks
,
clear_course_from_cache
from
lms.djangoapps.course_blocks.transformers.tests.test_helpers
import
CourseStructureTestCase
from
course_blocks.transformers.helpers
import
get_user_partition_groups
class
SplitTestTransformerTestCase
(
CourseStructureTestCase
):
"""
SplitTestTransformer Test
"""
def
setUp
(
self
):
"""
Setup course structure and create user for split test transformer test.
"""
super
(
SplitTestTransformerTestCase
,
self
)
.
setUp
()
# Set up user partitions and groups.
self
.
groups
=
[
Group
(
3
,
'Group A'
),
Group
(
4
,
'Group B'
)]
self
.
content_groups
=
[
3
,
4
]
self
.
split_test_user_partition
=
UserPartition
(
id
=
0
,
name
=
'Partition 2'
,
description
=
'This is partition 2'
,
groups
=
self
.
groups
,
scheme
=
RandomUserPartitionScheme
)
self
.
split_test_user_partition
.
scheme
.
name
=
"random"
# Build course.
self
.
course_hierarchy
=
self
.
get_test_course_hierarchy
()
self
.
blocks
=
self
.
build_course
(
self
.
course_hierarchy
)
self
.
course
=
self
.
blocks
[
'course'
]
clear_course_from_cache
(
self
.
course
.
id
)
# Set up user and enroll in course.
self
.
password
=
'test'
self
.
user
=
UserFactory
.
create
(
password
=
self
.
password
)
CourseEnrollmentFactory
.
create
(
user
=
self
.
user
,
course_id
=
self
.
course
.
id
,
is_active
=
True
)
self
.
transformation
=
[]
def
get_test_course_hierarchy
(
self
):
"""
Get a course hierarchy to test with.
Assumes self.split_test_user_partition has already been initialized.
Returns: dict[course_structure]
"""
return
{
'org'
:
'SplitTestTransformer'
,
'course'
:
'ST101F'
,
'run'
:
'test_run'
,
'user_partitions'
:
[
self
.
split_test_user_partition
],
'#ref'
:
'course'
,
'#children'
:
[
{
'#type'
:
'chapter'
,
'#ref'
:
'chapter1'
,
'#children'
:
[
{
'#type'
:
'sequential'
,
'#ref'
:
'lesson1'
,
'#children'
:
[
{
'#type'
:
'vertical'
,
'#ref'
:
'vertical1'
,
'#children'
:
[
{
'metadata'
:
{
'category'
:
'split_test'
},
'user_partition_id'
:
0
,
'group_id_to_child'
:
{
"3"
:
"i4x://SplitTestTransformer/ST101F/vertical/vertical_vertical2"
,
"4"
:
"i4x://SplitTestTransformer/ST101F/vertical/vertical_vertical3"
},
'#type'
:
'split_test'
,
'#ref'
:
'split_test1'
,
'#children'
:
[
{
'metadata'
:
{
'display_name'
:
"Group ID 3"
},
'#type'
:
'vertical'
,
'#ref'
:
'vertical2'
,
'#children'
:
[
{
'metadata'
:
{
'display_name'
:
"Group A"
},
'#type'
:
'html'
,
'#ref'
:
'html1'
,
}
]
},
{
'metadata'
:
{
'display_name'
:
"Group ID 4"
},
'#type'
:
'vertical'
,
'#ref'
:
'vertical3'
,
'#children'
:
[
{
'metadata'
:
{
'display_name'
:
"Group A"
},
'#type'
:
'html'
,
'#ref'
:
'html2'
,
}
]
}
]
}
],
}
],
}
],
}
]
}
def
add_user_to_splittest_group
(
self
,
assign
=
True
):
"""
Add user to split test, get group for him and update blocks.
"""
self
.
split_test_user_partition
.
scheme
.
get_group_for_user
(
CourseKey
.
from_string
(
unicode
(
self
.
course
.
id
)),
self
.
user
,
self
.
split_test_user_partition
,
assign
=
assign
,
)
store
=
modulestore
()
for
__
,
block
in
self
.
blocks
.
iteritems
():
block
.
save
()
store
.
update_item
(
block
,
self
.
user
.
id
)
def
test_course_structure_with_user_split_test
(
self
):
"""
Test course structure integrity if course has split test section
and user is not assigned to any group in user partition.
"""
self
.
transformation
=
SplitTestTransformer
()
# Add user to split test.
self
.
add_user_to_splittest_group
(
assign
=
False
)
raw_block_structure
=
get_course_blocks
(
self
.
user
,
self
.
course
.
location
,
transformers
=
{}
)
self
.
assertEqual
(
len
(
list
(
raw_block_structure
.
get_block_keys
())),
len
(
self
.
blocks
))
clear_course_from_cache
(
self
.
course
.
id
)
trans_block_structure
=
get_course_blocks
(
self
.
user
,
self
.
course
.
location
,
transformers
=
{
self
.
transformation
}
)
self
.
assertEqual
(
set
(
trans_block_structure
.
get_block_keys
()),
self
.
get_block_key_set
(
'course'
,
'chapter1'
,
'lesson1'
,
'vertical1'
,
'split_test1'
)
)
def
test_course_structure_with_user_split_test_group_assigned
(
self
):
"""
Test course structure integrity if course has split test section
and user is assigned to any group in user partition.
"""
self
.
transformation
=
SplitTestTransformer
()
# Add user to split test.
self
.
add_user_to_splittest_group
()
raw_block_structure
=
get_course_blocks
(
self
.
user
,
self
.
course
.
location
,
transformers
=
{}
)
self
.
assertEqual
(
len
(
list
(
raw_block_structure
.
get_block_keys
())),
len
(
self
.
blocks
))
clear_course_from_cache
(
self
.
course
.
id
)
trans_block_structure
=
get_course_blocks
(
self
.
user
,
self
.
course
.
location
,
transformers
=
{
self
.
transformation
}
)
user_groups
=
get_user_partition_groups
(
self
.
course
.
id
,
[
self
.
split_test_user_partition
],
self
.
user
)
for
group
in
user_groups
.
itervalues
():
if
group
.
id
==
3
:
self
.
assertEqual
(
set
(
trans_block_structure
.
get_block_keys
()),
self
.
get_block_key_set
(
'course'
,
'chapter1'
,
'lesson1'
,
'vertical1'
,
'split_test1'
,
'vertical2'
,
'html1'
)
)
else
:
self
.
assertEqual
(
set
(
trans_block_structure
.
get_block_keys
()),
self
.
get_block_key_set
(
'course'
,
'chapter1'
,
'lesson1'
,
'vertical1'
,
'split_test1'
,
'vertical3'
,
'html2'
)
)
lms/djangoapps/course_blocks/transformers/tests/test_user_partitions.py
View file @
a3a48a98
"""
Tests for UserPartitionTransform
ation
.
Tests for UserPartitionTransform
er
.
"""
from
openedx.core.djangoapps.course_groups.partition_scheme
import
CohortPartitionScheme
...
...
@@ -8,20 +8,21 @@ from openedx.core.djangoapps.course_groups.cohorts import add_user_to_cohort
from
openedx.core.djangoapps.course_groups.views
import
link_cohort_to_partition_group
from
student.tests.factories
import
UserFactory
from
student.tests.factories
import
CourseEnrollmentFactory
from
xmodule.modulestore.django
import
modulestore
from
xmodule.partitions.partitions
import
Group
,
UserPartition
from
course_blocks.transformers.user_partitions
import
UserPartitionTransformer
from
course_blocks.api
import
get_course_blocks
,
clear_course_from_cache
from
test_helpers
import
CourseStructureTestCase
from
lms.djangoapps.course_blocks.transformers.tests.
test_helpers
import
CourseStructureTestCase
class
UserPartitionTransformerTestCase
(
CourseStructureTestCase
):
"""
...
UserPartitionTransformer Test
"""
def
setUp
(
self
):
"""
Setup course structure and create user for user partition transformer test.
"""
super
(
UserPartitionTransformerTestCase
,
self
)
.
setUp
()
# Set up user partitions and groups.
...
...
@@ -73,8 +74,8 @@ class UserPartitionTransformerTestCase(CourseStructureTestCase):
'metadata'
:
{
'group_access'
:
{
0
:
[
0
,
1
,
2
]},
},
'#type'
:
'sequential'
,
'#ref'
:
'lesson1'
,
'#type'
:
'sequential'
,
'#ref'
:
'lesson1'
,
'#children'
:
[
{
'#type'
:
'vertical'
,
...
...
@@ -110,17 +111,11 @@ class UserPartitionTransformerTestCase(CourseStructureTestCase):
group
.
id
,
)
def
get_block_key_set
(
self
,
*
refs
):
def
test_course_structure_with_user_partition
(
self
):
"""
Gets the set of usage keys that correspond to the list of
#ref values as defined on self.blocks.
Returns: set[UsageKey]
Test course structure integrity if course has user partition section
and user is assigned to group in user partition.
"""
xblocks
=
(
self
.
blocks
[
ref
]
for
ref
in
refs
)
return
set
([
xblock
.
location
for
xblock
in
xblocks
])
def
test_course_structure_with_user_partition
(
self
):
self
.
transformation
=
UserPartitionTransformer
()
raw_block_structure
=
get_course_blocks
(
...
...
lms/djangoapps/course_blocks/transformers/user_partitions.py
View file @
a3a48a98
"""
..
.
User Partitions Transformer, used to filter course structure per user
.
"""
from
courseware.access
import
_has_access_to_course
from
openedx.core.lib.block_cache.transformer
import
BlockStructureTransformer
from
openedx.core.djangoapps.user_api.partition_schemes
import
RandomUserPartitionScheme
from
openedx.core.djangoapps.course_groups.partition_scheme
import
CohortPartitionScheme
# TODO 8874: Make it so we support all schemes instead of manually declaring them here.
INCLUDE_SCHEMES
=
[
CohortPartitionScheme
,
RandomUserPartitionScheme
,]
SCHEME_SUPPORTS_ASSIGNMENT
=
[
RandomUserPartitionScheme
,]
from
.helpers
import
get_user_partition_groups
class
MergedGroupAccess
(
object
):
"""
...
"""
# TODO 8874: Make it so LmsBlockMixin.merged_group_access use MergedGroupAccess
def
__init__
(
self
,
user_partitions
,
xblock
,
merged_parent_access_list
):
"""
Arguments:
...
...
@@ -98,7 +90,6 @@ class MergedGroupAccess(object):
for
partition_id
,
allowed_group_ids
in
self
.
_access
.
iteritems
():
# If the user is not assigned to a group for this partition, deny access.
# TODO 8874: Ensure that denying access to users who aren't in a group is the correct action.
if
partition_id
not
in
user_groups
:
return
False
...
...
@@ -122,49 +113,14 @@ class UserPartitionTransformer(BlockStructureTransformer):
"""
VERSION
=
1
@staticmethod
def
_get_user_partition_groups
(
course_key
,
user_partitions
,
user
):
"""
Collect group ID for each partition in this course for this user.
Arguments:
course_key (CourseKey)
user_partitions (list[UserPartition])
user (User)
Returns:
dict[int: Group]: Mapping from user partitions to the group to which
the user belongs in each partition. If the user isn't in a group
for a particular partition, then that partition's ID will not be
in the dict.
"""
partition_groups
=
{}
for
partition
in
user_partitions
:
if
partition
.
scheme
not
in
INCLUDE_SCHEMES
:
continue
group
=
partition
.
scheme
.
get_group_for_user
(
course_key
,
user
,
partition
,
**
({
'assign'
:
False
}
if
partition
.
scheme
in
SCHEME_SUPPORTS_ASSIGNMENT
else
{})
)
if
group
is
not
None
:
partition_groups
[
partition
.
id
]
=
group
return
partition_groups
@classmethod
def
collect
(
cls
,
block_structure
):
"""
Computes any information for each XBlock that's necessary to execute
this transform
ation's apply
method.
this transform
er's transform
method.
Arguments:
course_key (CourseKey)
block_structure (BlockStructure)
xblock_dict (dict[UsageKey: XBlock])
Returns:
dict[UsageKey: dict]
block_structure (BlockStructureCollectedData)
"""
# Because user partitions are course-wide, only store data for them on the root block.
root_block
=
block_structure
.
get_xblock
(
block_structure
.
root_block_key
)
...
...
@@ -192,21 +148,21 @@ class UserPartitionTransformer(BlockStructureTransformer):
def
transform
(
self
,
user_info
,
block_structure
):
"""
Mutates block_structure and block_data based on the given user_info.
Arguments:
user_info (object)
block_structure (BlockStructureCollectedData)
"""
# TODO 8874: Factor out functionality of UserPartitionTransformation.apply and access._has_group_access into a common utility function.
user_partitions
=
block_structure
.
get_transformer_data
(
self
,
'user_partitions'
)
# If there are no user partitions, this transformation is a no-op,
# so there is nothing to apply.
if
not
user_partitions
:
if
not
user_partitions
or
user_info
.
has_staff_access
:
return
user_groups
=
self
.
_
get_user_partition_groups
(
user_groups
=
get_user_partition_groups
(
user_info
.
course_key
,
user_partitions
,
user_info
.
user
)
if
not
user_info
.
has_staff_access
:
block_structure
.
remove_block_if
(
lambda
block_key
:
not
block_structure
.
get_transformer_block_data
(
block_key
,
self
,
'merged_group_access'
)
.
check_group_access
(
user_groups
)
)
block_structure
.
remove_block_if
(
lambda
block_key
:
not
block_structure
.
get_transformer_block_data
(
block_key
,
self
,
'merged_group_access'
)
.
check_group_access
(
user_groups
)
)
setup.py
View file @
a3a48a98
...
...
@@ -52,6 +52,8 @@ setup(
"visibility = lms.djangoapps.course_blocks.transformers.visibility:VisibilityTransformer"
,
"start_date = lms.djangoapps.course_blocks.transformers.start_date:StartDateTransformer"
,
"user_partitions = lms.djangoapps.course_blocks.transformers.user_partitions:UserPartitionTransformer"
,
"split_test = lms.djangoapps.course_blocks.transformers.split_test:SplitTestTransformer"
,
"library_content = lms.djangoapps.course_blocks.transformers.library_content:ContentLibraryTransformer"
,
],
}
)
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