Commit d5d7d654 by Nimisha Asthagiri

fixup! Split Test Transformer fixes.

parent 2021ba63
...@@ -10,7 +10,6 @@ from .transformers import ( ...@@ -10,7 +10,6 @@ from .transformers import (
start_date, start_date,
user_partitions, user_partitions,
visibility, visibility,
split_test,
library_content, library_content,
) )
from .user_info import CourseUserInfo from .user_info import CourseUserInfo
......
...@@ -46,7 +46,8 @@ class SplitTestTransformer(BlockStructureTransformer): ...@@ -46,7 +46,8 @@ class SplitTestTransformer(BlockStructureTransformer):
# set group access for each child # set group access for each child
for child_location in xblock.children: for child_location in xblock.children:
child = block_structure.get_xblock(child_location) child = block_structure.get_xblock(child_location)
child.group_access[partition_for_this_block.id] = [child_to_group[child_location]] group = child_to_group.get(child_location, None)
child.group_access[partition_for_this_block.id] = [group] if group else []
def transform(self, user_info, block_structure): def transform(self, user_info, block_structure):
""" """
......
...@@ -25,6 +25,13 @@ class CourseStructureTestCase(ModuleStoreTestCase): ...@@ -25,6 +25,13 @@ class CourseStructureTestCase(ModuleStoreTestCase):
self.user = UserFactory.create(password=self.password) self.user = UserFactory.create(password=self.password)
self.staff = UserFactory.create(password=self.password, is_staff=True) self.staff = UserFactory.create(password=self.password, is_staff=True)
def create_block_id(self, block_type, block_ref):
"""
Returns the block id (display name) that is used in the test
course structures for the given block type and block reference string.
"""
return '{}_{}'.format(block_type, block_ref)
def build_xblock(self, block_hierarchy, block_map, parent): def build_xblock(self, block_hierarchy, block_map, parent):
""" """
Build an XBlock, add it to block_map, and call build_xblock on the Build an XBlock, add it to block_map, and call build_xblock on the
...@@ -47,7 +54,7 @@ class CourseStructureTestCase(ModuleStoreTestCase): ...@@ -47,7 +54,7 @@ class CourseStructureTestCase(ModuleStoreTestCase):
kwargs['parent'] = parent kwargs['parent'] = parent
xblock = factory.create( xblock = factory.create(
display_name='{} {}'.format(block_type, block_ref), display_name=self.create_block_id(block_type, block_ref),
publish_item=True, publish_item=True,
**kwargs **kwargs
) )
...@@ -63,15 +70,35 @@ class CourseStructureTestCase(ModuleStoreTestCase): ...@@ -63,15 +70,35 @@ class CourseStructureTestCase(ModuleStoreTestCase):
The additional parents are obtained from the '#parents' field The additional parents are obtained from the '#parents' field
and is expected to be a list of '#ref' values of the parents. and is expected to be a list of '#ref' values of the parents.
Note: if a '#parents' field is found, the block is removed from
the course block since it is expected to not belong to the root.
If the block is meant to be a direct child of the course as well,
the course should be explicitly listed in '#parents'.
Arguments:
block_hierarchy (BlockStructureDict): Definition of block hierarchy.
block_map (dict[str: XBlock]): Mapping from '#ref' values to their XBlocks.
""" """
parents = block_hierarchy.get('#parents', []) parents = block_hierarchy.get('#parents', [])
if parents:
block_key = block_map[block_hierarchy['#ref']].location
# First remove the block from the course.
# It would be re-added to the course if the course was
# explicitly listed in parents.
course = modulestore().get_item(block_map['course'].location)
course.children.remove(block_key)
block_map['course'] = update_block(course)
# Add this to block to each listed parent.
for parent_ref in parents: for parent_ref in parents:
parent_block = block_map[parent_ref] parent_block = modulestore().get_item(block_map[parent_ref].location)
parent_block.children.append( parent_block.children.append(block_key)
block_map[block_hierarchy['#ref']].location block_map[parent_ref] = update_block(parent_block)
)
update_block(parent_block)
# recursively call the children
for child_hierarchy in block_hierarchy.get('#children', []): for child_hierarchy in block_hierarchy.get('#children', []):
self.add_parents(child_hierarchy, block_map) self.add_parents(child_hierarchy, block_map)
...@@ -278,3 +305,11 @@ def update_block(block): ...@@ -278,3 +305,11 @@ def update_block(block):
Helper method to update the block in the modulestore Helper method to update the block in the modulestore
""" """
return modulestore().update_item(block, 'test_user') return modulestore().update_item(block, 'test_user')
def create_location(org, course, run, block_type, block_id):
"""
Returns the usage key for the given key parameters using the
default modulestore
"""
return modulestore().make_course_key(org, course, run).make_usage_key(block_type, block_id)
\ No newline at end of file
""" """
Tests for SplitTestTransformer. Tests for SplitTestTransformer.
""" """
import ddt
import openedx.core.djangoapps.user_api.course_tag.api as course_tag_api
from openedx.core.djangoapps.user_api.partition_schemes import RandomUserPartitionScheme from openedx.core.djangoapps.user_api.partition_schemes import RandomUserPartitionScheme
from student.tests.factories import CourseEnrollmentFactory from student.tests.factories import CourseEnrollmentFactory
from xmodule.partitions.partitions import Group, UserPartition from xmodule.partitions.partitions import Group, UserPartition
from xmodule.modulestore.tests.factories import check_mongo_calls, check_mongo_calls_range
from ...api import get_course_blocks from ...api import get_course_blocks
from ..user_partitions import UserPartitionTransformer, get_user_partition_groups from ..user_partitions import UserPartitionTransformer, get_user_partition_groups
from .test_helpers import CourseStructureTestCase from .test_helpers import CourseStructureTestCase, create_location
@ddt.ddt
class SplitTestTransformerTestCase(CourseStructureTestCase): class SplitTestTransformerTestCase(CourseStructureTestCase):
""" """
SplitTestTransformer Test SplitTestTransformer Test
""" """
TEST_PARTITION_ID = 0
def setUp(self): def setUp(self):
""" """
Setup course structure and create user for split test transformer test. Setup course structure and create user for split test transformer test.
...@@ -21,13 +28,12 @@ class SplitTestTransformerTestCase(CourseStructureTestCase): ...@@ -21,13 +28,12 @@ class SplitTestTransformerTestCase(CourseStructureTestCase):
super(SplitTestTransformerTestCase, self).setUp() super(SplitTestTransformerTestCase, self).setUp()
# Set up user partitions and groups. # Set up user partitions and groups.
self.groups = [Group(3, 'Group A'), Group(4, 'Group B')] self.groups = [Group(1, 'Group 1'), Group(2, 'Group 2'), Group(3, 'Group 3')]
self.content_groups = [3, 4] self.split_test_user_partition_id = self.TEST_PARTITION_ID
self.split_test_user_partition_id = 0
self.split_test_user_partition = UserPartition( self.split_test_user_partition = UserPartition(
id=self.split_test_user_partition_id, id=self.split_test_user_partition_id,
name='Partition 2', name='Split Partition',
description='This is partition 2', description='This is split partition',
groups=self.groups, groups=self.groups,
scheme=RandomUserPartitionScheme scheme=RandomUserPartitionScheme
) )
...@@ -51,91 +57,170 @@ class SplitTestTransformerTestCase(CourseStructureTestCase): ...@@ -51,91 +57,170 @@ class SplitTestTransformerTestCase(CourseStructureTestCase):
Returns: dict[course_structure] Returns: dict[course_structure]
""" """
return [{
'org': 'SplitTestTransformer', org_name = 'SplitTestTransformer'
'course': 'ST101F', course_name = 'ST101F'
'run': 'test_run', run_name = 'test_run'
def location(block_ref, block_type='vertical'):
"""
Returns the usage key for the given block_type and block reference string in the test course.
"""
return create_location(
org_name, course_name, run_name, block_type, self.create_block_id(block_type, block_ref)
)
# course
# / | \
# / | \
# A BSplit CSplit
# / \ / | \ | \
# / \ / | \ | \
# D E[1] F[2] G[3] H[1] I[2]
# / \ \ |
# / \ \ |
# J KSplit \ L
# / | \ / \
# / | \ / \
# M[2] N[3] O P
#
return [
{
'org': org_name,
'course': course_name,
'run': run_name,
'user_partitions': [self.split_test_user_partition], 'user_partitions': [self.split_test_user_partition],
'#type': 'course', '#type': 'course',
'#ref': 'course', '#ref': 'course',
'#children': [ },
{ {
'#type': 'chapter', '#type': 'vertical',
'#ref': 'chapter1', '#ref': 'A',
'#children': [ '#children': [{'#type': 'vertical', '#ref': 'D'}],
},
{ {
'#type': 'sequential', '#type': 'split_test',
'#ref': 'lesson1', '#ref': 'BSplit',
'#children': [ 'metadata': {'category': 'split_test'},
'user_partition_id': self.TEST_PARTITION_ID,
'group_id_to_child': {
'1': location('E'),
'2': location('F'),
'3': location('G'),
},
'#children': [{'#type': 'vertical', '#ref': 'G'}],
},
{ {
'#type': 'vertical', '#type': 'vertical',
'#ref': 'vertical1', '#ref': 'E',
'#parents': ['A', 'BSplit'],
},
{
'#type': 'vertical',
'#ref': 'F',
'#parents': ['BSplit'],
'#children': [ '#children': [
{'#type': 'vertical', '#ref': 'J'},
],
},
{ {
'#type': 'split_test',
'#ref': 'KSplit',
'metadata': {'category': 'split_test'}, 'metadata': {'category': 'split_test'},
'user_partition_id': 0, 'user_partition_id': self.TEST_PARTITION_ID,
'group_id_to_child': { 'group_id_to_child': {
"3": "i4x://SplitTestTransformer/ST101F/vertical/vertical_vertical2", '2': location('M'),
"4": "i4x://SplitTestTransformer/ST101F/vertical/vertical_vertical3" '3': location('N'),
}, },
'#type': 'split_test', '#parents': ['F'],
'#ref': 'split_test1',
'#children': [
{
'metadata': {'display_name': "Group ID 3"},
'#type': 'vertical',
'#ref': 'vertical2',
'#children': [ '#children': [
{'#type': 'vertical', '#ref': 'M'},
{'#type': 'vertical', '#ref': 'N'},
],
},
{ {
'metadata': {'display_name': "Group A"}, '#type': 'split_test',
'#type': 'html', '#ref': 'CSplit',
'#ref': 'html1', 'metadata': {'category': 'split_test'},
} 'user_partition_id': self.TEST_PARTITION_ID,
] 'group_id_to_child': {
'1': location('H'),
'2': location('I'),
}, },
'#children': [
{'#type': 'vertical', '#ref': 'I'},
{ {
'metadata': {'display_name': "Group ID 4"},
'#type': 'vertical', '#type': 'vertical',
'#ref': 'vertical3', '#ref': 'H',
'#children': [ '#children': [
{ {
'metadata': {'display_name': "Group A"}, '#type': 'vertical',
'#type': 'html', '#ref': 'L',
'#ref': 'html2', '#children': [{'#type': 'vertical', '#ref': 'P',},],
} },
]
}
]
}
],
}
], ],
} },
], ],
} },
{
'#type': 'vertical',
'#ref': 'O',
'#parents': ['G', 'L'],
},
] ]
}]
def test_user(self): @ddt.data(
# Note: Theoretically, block E should be accessible by users
# not in Group 1, since there's an open path through block A.
# Since the split_test transformer automatically sets the block
# access on its children, it bypasses the paths via other
# parents. However, we don't think this is a use case we need to
# support for split_test components (since they are now deprecated
# in favor of content groups and user partitions).
(1, ('course', 'A', 'D', 'E', 'H', 'L', 'O', 'P',)),
(2, ('course', 'A', 'D', 'F', 'J', 'M', 'I',)),
(3, ('course', 'A', 'D', 'G', 'O',)),
)
@ddt.unpack
def test_user(self, group_id, expected_blocks):
course_tag_api.set_course_tag(
self.user,
self.course.id,
RandomUserPartitionScheme.key_for_partition(self.split_test_user_partition),
group_id,
)
block_structure1 = get_course_blocks(
self.user,
self.course.location,
transformers={self.transformer},
)
self.assertEqual(
set(block_structure1.get_block_keys()),
set(self.get_block_key_set(self.blocks, *expected_blocks)),
)
def test_user_randomly_assigned(self):
# user was randomly assigned to one of the groups # user was randomly assigned to one of the groups
user_groups = get_user_partition_groups( user_groups = get_user_partition_groups(
self.course.id, [self.split_test_user_partition], self.user self.course.id, [self.split_test_user_partition], self.user
) )
self.assertEquals(len(user_groups), 1) self.assertEquals(len(user_groups), 1)
group = user_groups[self.split_test_user_partition_id]
# determine expected blocks
expected_blocks = ['course', 'chapter1', 'lesson1', 'vertical1']
expected_blocks += (['vertical2', 'html1'] if group.id == 3 else ['vertical3', 'html2'])
# calling twice should result in the same block set # calling twice should result in the same block set
for _ in range(2): with check_mongo_calls_range(min_finds=1):
trans_block_structure = get_course_blocks( block_structure1 = get_course_blocks(
self.user, self.user,
self.course.location, self.course.location,
transformers={self.transformer}, transformers={self.transformer},
) )
self.assertEqual( # with check_mongo_calls(0): TODO - debug issue with pickling
set(trans_block_structure.get_block_keys()), # block_structure2 = get_course_blocks(
set(self.get_block_key_set(self.blocks, *expected_blocks)) # self.user,
) # self.course.location,
# transformers={self.transformer},
# )
# self.assertEqual(
# set(block_structure1.get_block_keys()),
# set(block_structure2.get_block_keys()),
# )
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
Tests for UserPartitionTransformer. Tests for UserPartitionTransformer.
""" """
import ddt
from openedx.core.djangoapps.course_groups.partition_scheme import CohortPartitionScheme from openedx.core.djangoapps.course_groups.partition_scheme import CohortPartitionScheme
from openedx.core.djangoapps.course_groups.tests.helpers import CohortFactory, config_course_cohorts from openedx.core.djangoapps.course_groups.tests.helpers import CohortFactory, config_course_cohorts
from openedx.core.djangoapps.course_groups.cohorts import add_user_to_cohort from openedx.core.djangoapps.course_groups.cohorts import add_user_to_cohort
...@@ -10,10 +12,11 @@ from student.tests.factories import CourseEnrollmentFactory ...@@ -10,10 +12,11 @@ from student.tests.factories import CourseEnrollmentFactory
from xmodule.partitions.partitions import Group, UserPartition from xmodule.partitions.partitions import Group, UserPartition
from course_blocks.transformers.user_partitions import UserPartitionTransformer from course_blocks.transformers.user_partitions import UserPartitionTransformer
from course_blocks.api import get_course_blocks, clear_course_from_cache from course_blocks.api import get_course_blocks
from lms.djangoapps.course_blocks.transformers.tests.test_helpers import CourseStructureTestCase from lms.djangoapps.course_blocks.transformers.tests.test_helpers import CourseStructureTestCase
@ddt.ddt
class UserPartitionTransformerTestCase(CourseStructureTestCase): class UserPartitionTransformerTestCase(CourseStructureTestCase):
""" """
UserPartitionTransformer Test UserPartitionTransformer Test
...@@ -57,8 +60,6 @@ class UserPartitionTransformerTestCase(CourseStructureTestCase): ...@@ -57,8 +60,6 @@ class UserPartitionTransformerTestCase(CourseStructureTestCase):
group.id, group.id,
) )
add_user_to_cohort(self.cohorts[0], self.user.username)
self.transformer = UserPartitionTransformer() self.transformer = UserPartitionTransformer()
def get_course_hierarchy(self): def get_course_hierarchy(self):
...@@ -159,11 +160,20 @@ class UserPartitionTransformerTestCase(CourseStructureTestCase): ...@@ -159,11 +160,20 @@ class UserPartitionTransformerTestCase(CourseStructureTestCase):
}, },
] ]
def test_user_assigned(self): @ddt.data(
(None, ('course', 'B', 'O')),
(1, ('course', 'A', 'B', 'C', 'E', 'F', 'G', 'J', 'L', 'M', 'O')),
(2, ('course', 'A', 'B', 'C', 'D', 'E', 'F', 'H', 'I', 'J', 'M', 'O')),
(3, ('course', 'A', 'B', 'D', 'E', 'I', 'J', 'O')),
(4, ('course', 'B', 'O')),
)
@ddt.unpack
def test_user_assigned(self, group_id, expected_blocks):
""" """
Test when user is assigned to group in user partition. Test when user is assigned to group in user partition.
""" """
# TODO ddt with testing user in different groups if group_id:
add_user_to_cohort(self.cohorts[group_id-1], self.user.username)
trans_block_structure = get_course_blocks( trans_block_structure = get_course_blocks(
self.user, self.user,
...@@ -172,7 +182,7 @@ class UserPartitionTransformerTestCase(CourseStructureTestCase): ...@@ -172,7 +182,7 @@ class UserPartitionTransformerTestCase(CourseStructureTestCase):
) )
self.assertSetEqual( self.assertSetEqual(
set(trans_block_structure.get_block_keys()), set(trans_block_structure.get_block_keys()),
self.get_block_key_set(self.blocks, 'course', 'A', 'B', 'C', 'E', 'F', 'G', 'J', 'L', 'M', 'O') self.get_block_key_set(self.blocks, *expected_blocks)
) )
def test_staff_user(self): def test_staff_user(self):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment