def test_remove_block(self, keep_descendants, block_to_remove, children_map): ### skip test if invalid if (block_to_remove >= len(children_map)) or (keep_descendants and block_to_remove == 0): return ### create structure block_structure = self.create_block_structure(BlockStructureBlockData, children_map) parents_map = self.get_parents_map(children_map) ### verify blocks pre-exist self.assert_block_structure(block_structure, children_map) ### remove block block_structure.remove_block(block_to_remove, keep_descendants) missing_blocks = [block_to_remove] ### compute and verify updated children_map removed_children_map = deepcopy(children_map) removed_children_map[block_to_remove] = [] for parent in parents_map[block_to_remove]: removed_children_map[parent].remove(block_to_remove) if keep_descendants: # update the graph connecting the old parents to the old children for child in children_map[block_to_remove]: for parent in parents_map[block_to_remove]: removed_children_map[parent].append(child) self.assert_block_structure(block_structure, removed_children_map, missing_blocks) ### prune the structure block_structure._prune_unreachable() ### compute and verify updated children_map pruned_children_map = deepcopy(removed_children_map) if not keep_descendants: pruned_parents_map = self.get_parents_map(pruned_children_map) # update all descendants for child in children_map[block_to_remove]: # if the child has another parent, continue if pruned_parents_map[child]: continue for block in traverse_post_order( child, get_children=lambda block: pruned_children_map[block]): # add descendant to missing blocks and empty its # children missing_blocks.append(block) pruned_children_map[block] = [] self.assert_block_structure(block_structure, pruned_children_map, missing_blocks)
def test_remove_block(self, keep_descendants, block_to_remove, children_map): ### skip test if invalid if (block_to_remove >= len(children_map)) or (keep_descendants and block_to_remove == 0): return ### create structure block_structure = self.create_block_structure(children_map) parents_map = self.get_parents_map(children_map) ### verify blocks pre-exist self.assert_block_structure(block_structure, children_map) ### remove block block_structure.remove_block(block_to_remove, keep_descendants) missing_blocks = [block_to_remove] ### compute and verify updated children_map removed_children_map = deepcopy(children_map) removed_children_map[block_to_remove] = [] for parent in parents_map[block_to_remove]: removed_children_map[parent].remove(block_to_remove) if keep_descendants: # update the graph connecting the old parents to the old children for child in children_map[block_to_remove]: for parent in parents_map[block_to_remove]: removed_children_map[parent].append(child) self.assert_block_structure(block_structure, removed_children_map, missing_blocks) ### prune the structure block_structure._prune_unreachable() ### compute and verify updated children_map pruned_children_map = deepcopy(removed_children_map) if not keep_descendants: pruned_parents_map = self.get_parents_map(pruned_children_map) # update all descendants for child in children_map[block_to_remove]: # if the child has another parent, continue if pruned_parents_map[child]: continue for block in traverse_post_order(child, get_children=lambda block: pruned_children_map[block]): # add descendant to missing blocks and empty its # children missing_blocks.append(block) pruned_children_map[block] = [] self.assert_block_structure(block_structure, pruned_children_map, missing_blocks)
def post_order_traversal( self, filter_func=None, ): """ Performs a post-order sort of the block structure and yields the usage_key of each block as it is encountered. Arguments: See the description in openedx.core.lib.graph_traversals.traverse_post_order. Returns: generator - A generator object created from the traverse_post_order method. """ return traverse_post_order( start_node=self.root_block_usage_key, get_children=self.get_children, filter_func=filter_func, )