From fcbc8446d6ee9ddb22bce3399ef5ff6bc2009753 Mon Sep 17 00:00:00 2001 From: "E. Kolpakov" Date: Wed, 24 Dec 2014 11:33:53 +0300 Subject: [PATCH] Problem type filtering on `update_children` event --- common/lib/xmodule/xmodule/capa_module.py | 9 ++++++++ .../xmodule/xmodule/library_content_module.py | 19 +--------------- common/lib/xmodule/xmodule/library_tools.py | 22 ++++++++++++++++--- 3 files changed, 29 insertions(+), 21 deletions(-) diff --git a/common/lib/xmodule/xmodule/capa_module.py b/common/lib/xmodule/xmodule/capa_module.py index 776dc36e3f..4c7f785c6d 100644 --- a/common/lib/xmodule/xmodule/capa_module.py +++ b/common/lib/xmodule/xmodule/capa_module.py @@ -2,10 +2,12 @@ import json import logging import sys +from lxml import etree from pkg_resources import resource_string from .capa_base import CapaMixin, CapaFields, ComplexEncoder +from capa import inputtypes from .progress import Progress from xmodule.x_module import XModule, module_attr from xmodule.raw_module import RawDescriptor @@ -172,6 +174,13 @@ class CapaDescriptor(CapaFields, RawDescriptor): ]) return non_editable_fields + @property + def problem_types(self): + """ Low-level problem type introspection for content libraries filtering by problem type """ + tree = etree.XML(self.data) + registered_tas = inputtypes.registry.registered_tags() + return set([node.tag for node in tree.iter() if node.tag in registered_tas]) + # Proxy to CapaModule for access to any of its attributes answer_available = module_attr('answer_available') check_button_name = module_attr('check_button_name') diff --git a/common/lib/xmodule/xmodule/library_content_module.py b/common/lib/xmodule/xmodule/library_content_module.py index e2e2481324..11d23106f4 100644 --- a/common/lib/xmodule/xmodule/library_content_module.py +++ b/common/lib/xmodule/xmodule/library_content_module.py @@ -222,23 +222,6 @@ class LibraryContentModule(LibraryContentFields, XModule, StudioEditableModule): as children of this block, but only a subset of those children are shown to any particular student. """ - def _filter_children(self, child_locator): - """ - Filters children by CAPA problem type, if configured - """ - if self.capa_type == ANY_CAPA_TYPE_VALUE: - return True - - if child_locator.block_type != CAPA_BLOCK_TYPE: - return False - - block = self.runtime.get_block(child_locator) - - if not hasattr(block, 'lcp'): - return True - - return any(self.capa_type in capa_input.tags for capa_input in block.lcp.inputs.values()) - def selected_children(self): """ Returns a set() of block_ids indicating which of the possible children @@ -255,7 +238,7 @@ class LibraryContentModule(LibraryContentFields, XModule, StudioEditableModule): return self._selected_set # pylint: disable=access-member-before-definition # Determine which of our children we will show: selected = set(tuple(k) for k in self.selected) # set of (block_type, block_id) tuples - valid_block_keys = set([(c.block_type, c.block_id) for c in self.children if self._filter_children(c)]) # pylint: disable=no-member + valid_block_keys = set([(c.block_type, c.block_id) for c in self.children]) # pylint: disable=no-member # Remove any selected blocks that are no longer valid: selected -= (selected - valid_block_keys) # If max_count has been decreased, we may have to drop some previously selected blocks: diff --git a/common/lib/xmodule/xmodule/library_tools.py b/common/lib/xmodule/xmodule/library_tools.py index f8dcadf80e..d0e6768ed8 100644 --- a/common/lib/xmodule/xmodule/library_tools.py +++ b/common/lib/xmodule/xmodule/library_tools.py @@ -5,8 +5,9 @@ import hashlib from django.core.exceptions import PermissionDenied from opaque_keys.edx.locator import LibraryLocator from xblock.fields import Scope -from xmodule.library_content_module import LibraryVersionReference +from xmodule.library_content_module import LibraryVersionReference, ANY_CAPA_TYPE_VALUE from xmodule.modulestore.exceptions import ItemNotFoundError +from xmodule.capa_module import CapaDescriptor class LibraryToolsService(object): @@ -45,6 +46,18 @@ class LibraryToolsService(object): return library.location.library_key.version_guid return None + def _filter_child(self, dest_block, child_descriptor): + """ + Filters children by CAPA problem type, if configured + """ + if dest_block.capa_type == ANY_CAPA_TYPE_VALUE: + return True + + if not isinstance(child_descriptor, CapaDescriptor): + return False + + return dest_block.capa_type in child_descriptor.problem_types + def update_children(self, dest_block, user_id, user_perms=None, update_db=True): """ This method is to be used when any of the libraries that a LibraryContentModule @@ -91,13 +104,16 @@ class LibraryToolsService(object): new_libraries = [] for library_key, library in libraries: - def copy_children_recursively(from_block): + def copy_children_recursively(from_block, filter_problem_type=True): """ Internal method to copy blocks from the library recursively """ new_children = [] for child_key in from_block.children: child = self.store.get_item(child_key, depth=9) + + if filter_problem_type and not self._filter_child(dest_block, child): + continue # We compute a block_id for each matching child block found in the library. # block_ids are unique within any branch, but are not unique per-course or globally. # We need our block_ids to be consistent when content in the library is updated, so @@ -125,7 +141,7 @@ class LibraryToolsService(object): ) new_children.append(new_child_info.location) return new_children - root_children.extend(copy_children_recursively(from_block=library)) + root_children.extend(copy_children_recursively(from_block=library, filter_problem_type=True)) new_libraries.append(LibraryVersionReference(library_key, library.location.library_key.version_guid)) dest_block.source_libraries = new_libraries dest_block.children = root_children