Commit d5d7d654 by Nimisha Asthagiri

fixup! Split Test Transformer fixes.

parent 2021ba63
......@@ -10,7 +10,6 @@ from .transformers import (
start_date,
user_partitions,
visibility,
split_test,
library_content,
)
from .user_info import CourseUserInfo
......
......@@ -46,7 +46,8 @@ class SplitTestTransformer(BlockStructureTransformer):
# set group access for each child
for child_location in xblock.children:
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):
"""
......
......@@ -25,6 +25,13 @@ class CourseStructureTestCase(ModuleStoreTestCase):
self.user = UserFactory.create(password=self.password)
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):
"""
Build an XBlock, add it to block_map, and call build_xblock on the
......@@ -47,7 +54,7 @@ class CourseStructureTestCase(ModuleStoreTestCase):
kwargs['parent'] = parent
xblock = factory.create(
display_name='{} {}'.format(block_type, block_ref),
display_name=self.create_block_id(block_type, block_ref),
publish_item=True,
**kwargs
)
......@@ -63,15 +70,35 @@ class CourseStructureTestCase(ModuleStoreTestCase):
The additional parents are obtained from the '#parents' field
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', [])
for parent_ref in parents:
parent_block = block_map[parent_ref]
parent_block.children.append(
block_map[block_hierarchy['#ref']].location
)
update_block(parent_block)
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:
parent_block = modulestore().get_item(block_map[parent_ref].location)
parent_block.children.append(block_key)
block_map[parent_ref] = update_block(parent_block)
# recursively call the children
for child_hierarchy in block_hierarchy.get('#children', []):
self.add_parents(child_hierarchy, block_map)
......@@ -278,3 +305,11 @@ def update_block(block):
Helper method to update the block in the modulestore
"""
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.
"""
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 student.tests.factories import CourseEnrollmentFactory
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 ..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):
"""
SplitTestTransformer Test
"""
TEST_PARTITION_ID = 0
def setUp(self):
"""
Setup course structure and create user for split test transformer test.
......@@ -21,13 +28,12 @@ class SplitTestTransformerTestCase(CourseStructureTestCase):
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_id = 0
self.groups = [Group(1, 'Group 1'), Group(2, 'Group 2'), Group(3, 'Group 3')]
self.split_test_user_partition_id = self.TEST_PARTITION_ID
self.split_test_user_partition = UserPartition(
id=self.split_test_user_partition_id,
name='Partition 2',
description='This is partition 2',
name='Split Partition',
description='This is split partition',
groups=self.groups,
scheme=RandomUserPartitionScheme
)
......@@ -51,91 +57,170 @@ class SplitTestTransformerTestCase(CourseStructureTestCase):
Returns: dict[course_structure]
"""
return [{
'org': 'SplitTestTransformer',
'course': 'ST101F',
'run': 'test_run',
'user_partitions': [self.split_test_user_partition],
'#type': 'course',
'#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 test_user(self):
org_name = 'SplitTestTransformer'
course_name = 'ST101F'
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],
'#type': 'course',
'#ref': 'course',
},
{
'#type': 'vertical',
'#ref': 'A',
'#children': [{'#type': 'vertical', '#ref': 'D'}],
},
{
'#type': 'split_test',
'#ref': 'BSplit',
'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',
'#ref': 'E',
'#parents': ['A', 'BSplit'],
},
{
'#type': 'vertical',
'#ref': 'F',
'#parents': ['BSplit'],
'#children': [
{'#type': 'vertical', '#ref': 'J'},
],
},
{
'#type': 'split_test',
'#ref': 'KSplit',
'metadata': {'category': 'split_test'},
'user_partition_id': self.TEST_PARTITION_ID,
'group_id_to_child': {
'2': location('M'),
'3': location('N'),
},
'#parents': ['F'],
'#children': [
{'#type': 'vertical', '#ref': 'M'},
{'#type': 'vertical', '#ref': 'N'},
],
},
{
'#type': 'split_test',
'#ref': 'CSplit',
'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'},
{
'#type': 'vertical',
'#ref': 'H',
'#children': [
{
'#type': 'vertical',
'#ref': 'L',
'#children': [{'#type': 'vertical', '#ref': 'P',},],
},
],
},
],
},
{
'#type': 'vertical',
'#ref': 'O',
'#parents': ['G', 'L'],
},
]
@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_groups = get_user_partition_groups(
self.course.id, [self.split_test_user_partition], self.user
)
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
for _ in range(2):
trans_block_structure = get_course_blocks(
with check_mongo_calls_range(min_finds=1):
block_structure1 = get_course_blocks(
self.user,
self.course.location,
transformers={self.transformer},
)
self.assertEqual(
set(trans_block_structure.get_block_keys()),
set(self.get_block_key_set(self.blocks, *expected_blocks))
)
# with check_mongo_calls(0): TODO - debug issue with pickling
# block_structure2 = get_course_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 @@
Tests for UserPartitionTransformer.
"""
import ddt
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.cohorts import add_user_to_cohort
......@@ -10,10 +12,11 @@ from student.tests.factories import CourseEnrollmentFactory
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 course_blocks.api import get_course_blocks
from lms.djangoapps.course_blocks.transformers.tests.test_helpers import CourseStructureTestCase
@ddt.ddt
class UserPartitionTransformerTestCase(CourseStructureTestCase):
"""
UserPartitionTransformer Test
......@@ -57,8 +60,6 @@ class UserPartitionTransformerTestCase(CourseStructureTestCase):
group.id,
)
add_user_to_cohort(self.cohorts[0], self.user.username)
self.transformer = UserPartitionTransformer()
def get_course_hierarchy(self):
......@@ -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.
"""
# 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(
self.user,
......@@ -172,7 +182,7 @@ class UserPartitionTransformerTestCase(CourseStructureTestCase):
)
self.assertSetEqual(
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):
......
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