def test_xml_export_import_cycle(self): """ Test the export-import cycle. """ # Children will only set after calling this. self.lc_block.refresh_children() lc_block = self.store.get_item(self.lc_block.location) expected_olx = ( '<library_content display_name="{block.display_name}" max_count="{block.max_count}"' ' source_library_id="{block.source_library_id}" source_library_version="{block.source_library_version}">\n' ' <html url_name="{block.children[0].block_id}"/>\n' ' <html url_name="{block.children[1].block_id}"/>\n' ' <html url_name="{block.children[2].block_id}"/>\n' ' <html url_name="{block.children[3].block_id}"/>\n' '</library_content>\n' ).format( block=lc_block, ) export_fs = MemoryFS() # Set the virtual FS to export the olx to. lc_block.runtime._descriptor_system.export_fs = export_fs # pylint: disable=protected-access # Export the olx. node = etree.Element("unknown_root") lc_block.add_xml_to_node(node) # Read it back with export_fs.open('{dir}/{file_name}.xml'.format( dir=lc_block.scope_ids.usage_id.block_type, file_name=lc_block.scope_ids.usage_id.block_id )) as f: exported_olx = f.read() # And compare. self.assertEqual(exported_olx, expected_olx) runtime = TestImportSystem(load_error_modules=True, course_id=lc_block.location.course_key) runtime.resources_fs = export_fs # Now import it. olx_element = etree.fromstring(exported_olx) id_generator = Mock() imported_lc_block = LibraryContentBlock.parse_xml(olx_element, runtime, None, id_generator) # Check the new XBlock has the same properties as the old one. self.assertEqual(imported_lc_block.display_name, lc_block.display_name) self.assertEqual(imported_lc_block.source_library_id, lc_block.source_library_id) self.assertEqual(imported_lc_block.source_library_version, lc_block.source_library_version) self.assertEqual(imported_lc_block.mode, lc_block.mode) self.assertEqual(imported_lc_block.max_count, lc_block.max_count) self.assertEqual(imported_lc_block.capa_type, lc_block.capa_type) self.assertEqual(len(imported_lc_block.children), 4) self.assertEqual(imported_lc_block.children, lc_block.children)
def _publish_events(self, block_structure, location, previous_count, max_count, block_keys, user_id): """ Helper method to publish events for analytics purposes """ def format_block_keys(keys): """ Helper function to format block keys """ json_result = [] for key in keys: info = block_structure.get_transformer_block_field( key, ContentLibraryTransformer, 'block_analytics_summary') json_result.append(info) return json_result def publish_event(event_name, result, **kwargs): """ Helper function to publish an event for analytics purposes """ event_data = { "location": six.text_type(location), "previous_count": previous_count, "result": result, "max_count": max_count, } event_data.update(kwargs) context = contexts.course_context_from_course_id( location.course_key) if user_id: context['user_id'] = user_id full_event_name = "edx.librarycontentblock.content.{}".format( event_name) with tracker.get_tracker().context(full_event_name, context): tracker.emit(full_event_name, event_data) LibraryContentBlock.publish_selected_children_events( block_keys, format_block_keys, publish_event, )
def test_xml_export_import_cycle(self): """ Test the export-import cycle. """ # Read back the olx. with self.export_fs.open('{dir}/{file_name}.xml'.format( dir=self.lc_block.scope_ids.usage_id.block_type, file_name=self.lc_block.scope_ids.usage_id.block_id )) as f: exported_olx = f.read() # And compare. assert exported_olx == self.expected_olx # Now import it. olx_element = etree.fromstring(exported_olx) imported_lc_block = LibraryContentBlock.parse_xml(olx_element, self.runtime, None, self.id_generator) self._verify_xblock_properties(imported_lc_block)
def test_xml_import_with_comments(self): """ Test that XML comments within LibraryContentBlock are ignored during the import. """ olx_with_comments = ( '<!-- Comment -->\n' '<library_content display_name="{block.display_name}" max_count="{block.max_count}"' ' source_library_id="{block.source_library_id}" source_library_version="{block.source_library_version}">\n' '<!-- Comment -->\n' ' <html url_name="{block.children[0].block_id}"/>\n' ' <html url_name="{block.children[1].block_id}"/>\n' ' <html url_name="{block.children[2].block_id}"/>\n' ' <html url_name="{block.children[3].block_id}"/>\n' '</library_content>\n' ).format( block=self.lc_block, ) # Import the olx. olx_element = etree.fromstring(olx_with_comments) imported_lc_block = LibraryContentBlock.parse_xml(olx_element, self.runtime, None, self.id_generator) self._verify_xblock_properties(imported_lc_block)
def transform_block_filters(self, usage_info, block_structure): all_library_children = set() all_selected_children = set() for block_key in block_structure: if block_key.block_type != 'library_content': continue library_children = block_structure.get_children(block_key) if library_children: all_library_children.update(library_children) selected = [] mode = block_structure.get_xblock_field(block_key, 'mode') max_count = block_structure.get_xblock_field( block_key, 'max_count') # Retrieve "selected" json from LMS MySQL database. state_dict = get_student_module_as_dict( usage_info.user, usage_info.course_key, block_key) for selected_block in state_dict.get('selected', []): # Add all selected entries for this user for this # library module to the selected list. block_type, block_id = selected_block usage_key = usage_info.course_key.make_usage_key( block_type, block_id) if usage_key in library_children: selected.append(selected_block) # Update selected previous_count = len(selected) block_keys = LibraryContentBlock.make_selection( selected, library_children, max_count, mode) selected = block_keys['selected'] # Save back any changes if any(block_keys[changed] for changed in ('invalid', 'overlimit', 'added')): state_dict['selected'] = selected StudentModule.save_state( student=usage_info.user, course_id=usage_info.course_key, module_state_key=block_key, defaults={ 'state': json.dumps(state_dict), }, ) # publish events for analytics self._publish_events( block_structure, block_key, previous_count, max_count, block_keys, usage_info.user.id, ) all_selected_children.update( usage_info.course_key.make_usage_key(s[0], s[1]) for s in selected) def check_child_removal(block_key): """ Return True 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 all_library_children: return False if block_key in all_selected_children: return False return True return [block_structure.create_removal_filter(check_child_removal)]