Example #1
0
    def test_static_tabs_import(self):
        """Make sure that the static tabs are imported correctly"""

        modulestore = XMLModuleStore(DATA_DIR, source_dirs=['toy'])

        location_tab_syllabus = BlockUsageLocator(CourseLocator("edX", "toy", "2012_Fall", deprecated=True),
                                                  "static_tab", "syllabus", deprecated=True)
        toy_tab_syllabus = modulestore.get_item(location_tab_syllabus)
        assert toy_tab_syllabus.display_name == 'Syllabus'
        assert toy_tab_syllabus.course_staff_only is False

        location_tab_resources = BlockUsageLocator(CourseLocator("edX", "toy", "2012_Fall", deprecated=True),
                                                   "static_tab", "resources", deprecated=True)
        toy_tab_resources = modulestore.get_item(location_tab_resources)
        assert toy_tab_resources.display_name == 'Resources'
        assert toy_tab_resources.course_staff_only is True
Example #2
0
def get_transcript(course_id,
                   block_id,
                   lang=None,
                   output_format=Transcript.SRT,
                   is_bumper=False):
    """
    Get video transcript from edx-val or content store.

    Arguments:
        course_id (CourseLocator): course identifier
        block_id (unicode): a unique identifier for an item in modulestore
        lang (unicode): transcript language
        output_format (unicode): transcript output format
        is_bumper (bool): indicates bumper video

    Returns:
        tuple containing content, filename, mimetype
    """
    usage_key = BlockUsageLocator(course_id,
                                  block_type='video',
                                  block_id=block_id)
    video_descriptor = modulestore().get_item(usage_key)

    try:
        return get_transcript_from_val(video_descriptor.edx_video_id, lang,
                                       output_format)
    except NotFoundError:
        return get_transcript_from_contentstore(video_descriptor,
                                                lang,
                                                output_format=output_format,
                                                is_bumper=is_bumper)
Example #3
0
def track_user_event(user, event_name, data, page):
    """
    Marks a user's progress in the user's CompletionProfile whenever an
    appropriate event is logged, and sends the report to the third-party API.

    Args:
        user (User): the user object.
        event_name (str): the name of the logged event. Event names are specific to
            edX events and are mapped to the events tracked for the completion report.
        data (str or dict): in the event of a watched video, edX logs data in a dict
            where the `id` key contains the video block ID. For problems this argument
            contains the usage key.
        page (str): URL where the event was triggered. Used to extract the course ID.
    """
    if event_name in EVENT_BLOCK_MAP:
        block_type = EVENT_BLOCK_MAP[event_name]
        course_id = extract_course_id_from_url(page)
        course_key = CourseKey.from_string(course_id)
        if block_type == 'problem':
            data_id = extract_problem_id(data)
        elif block_type == 'video':
            data_id = data['id']

        usage_key = BlockUsageLocator(course_key, block_type, data_id)
        CompletionProfile.mark_progress(user, course_key, usage_key.block_id)
    def setUp(self):
        super().setUp()
        system = get_test_descriptor_system(render_template=Mock())
        self.tabs = [{
            'name': "Test_css",
            'template': "tabs/codemirror-edit.html",
            'current': True,
            'css': {
                'scss': [
                    resource_string(
                        __name__, 'test_files/test_tabseditingdescriptor.scss')
                ],
                'css': [
                    resource_string(
                        __name__, 'test_files/test_tabseditingdescriptor.css')
                ]
            }
        }, {
            'name': "Subtitles",
            'template': "video/subtitles.html",
        }, {
            'name': "Settings",
            'template': "tabs/video-metadata-edit-tab.html"
        }]

        TabsEditingDescriptor.tabs = self.tabs
        self.descriptor = system.construct_xblock_from_class(
            TabsEditingDescriptor,
            scope_ids=ScopeIds(
                None, None, None,
                BlockUsageLocator(
                    CourseLocator('org', 'course', 'run', branch='revision'),
                    'category', 'name')),
            field_data=DictFieldData({}),
        )
def _calculate_score_for_modules(user_id, course, modules):
    """
    Calculates the cumulative score (percent) of the given modules
    """

    # removing branch and version from exam modules locator
    # otherwise student module would not return scores since module usage keys would not match
    modules = [m for m in modules]
    locations = [
        BlockUsageLocator(
            course_key=course.id,
            block_type=module.location.block_type,
            block_id=module.location.block_id
        )
        if isinstance(module.location, BlockUsageLocator) and module.location.version
        else module.location
        for module in modules
    ]

    scores_client = ScoresClient(course.id, user_id)
    scores_client.fetch_scores(locations)

    # Iterate over all of the exam modules to get score percentage of user for each of them
    module_percentages = []
    ignore_categories = ['course', 'chapter', 'sequential', 'vertical', 'randomize', 'library_content']
    for index, module in enumerate(modules):
        if module.category not in ignore_categories and (module.graded or module.has_score):
            module_score = scores_client.get(locations[index])
            if module_score:
                correct = module_score.correct or 0
                total = module_score.total or 1
                module_percentages.append(correct / total)

    return sum(module_percentages) / float(len(module_percentages)) if module_percentages else 0
Example #6
0
 def test_conditional_module_with_empty_sources_list(self):
     """
     If a ConditionalDescriptor is initialized with an empty sources_list, we assert that the sources_list is set
     via generating UsageKeys from the values in xml_attributes['sources']
     """
     dummy_system = Mock()
     dummy_location = BlockUsageLocator(
         CourseLocator("edX", "conditional_test", "test_run"),
         "conditional", "SampleConditional")
     dummy_scope_ids = ScopeIds(None, None, dummy_location, dummy_location)
     dummy_field_data = DictFieldData({
         'data': '<conditional/>',
         'xml_attributes': {
             'sources': 'i4x://HarvardX/ER22x/poll_question/T15_poll'
         },
         'children': None,
     })
     conditional = ConditionalDescriptor(
         dummy_system,
         dummy_field_data,
         dummy_scope_ids,
     )
     new_run = conditional.location.course_key.run
     self.assertEqual(
         conditional.sources_list[0],
         # Matching what is in ConditionalDescriptor.__init__.
         BlockUsageLocator.from_string(conditional.xml_attributes['sources']
                                       ).replace(run=new_run))
Example #7
0
 def create_location(self, block_type, block_id):
     """
     Create a new BlockUsageLocation with the given type and ID.
     """
     return BlockUsageLocator(course_key=self.course_key,
                              block_type=block_type,
                              block_id=block_id)
Example #8
0
 def setUp(self):
     super(SignAndSendReplaceResultTest, self).setUp()
     self.course_key = CourseLocator(
         org='some_org',
         course='some_course',
         run='some_run'
     )
     self.usage_key = BlockUsageLocator(
         course_key=self.course_key,
         block_type='problem',
         block_id='block_id'
     )
     self.user = UserFactory.create()
     consumer = LtiConsumer(
         consumer_name='consumer',
         consumer_key='consumer_key',
         consumer_secret='secret'
     )
     consumer.save()
     outcome = OutcomeService(
         lis_outcome_service_url='http://example.com/service_url',
         lti_consumer=consumer,
     )
     outcome.save()
     self.assignment = GradedAssignment(
         user=self.user,
         course_key=self.course_key,
         usage_key=self.usage_key,
         outcome_service=outcome,
         lis_result_sourcedid='sourcedid',
     )
     self.assignment.save()
Example #9
0
    def test_xml_import_export_cycle(self):
        """
        Test the import export cycle.
        """

        runtime = get_test_descriptor_system()
        runtime.export_fs = MemoryFS()

        original_xml = (
            '<word_cloud display_name="Favorite Fruits" display_student_percents="false" '
            'instructions="What are your favorite fruits?" num_inputs="3" num_top_words="100"/>\n'
        )

        olx_element = etree.fromstring(original_xml)
        id_generator = Mock()
        block = WordCloudBlock.parse_xml(olx_element, runtime, None,
                                         id_generator)
        block.location = BlockUsageLocator(
            CourseLocator('org', 'course', 'run', branch='revision'),
            'word_cloud', 'block_id')

        assert block.display_name == 'Favorite Fruits'
        assert not block.display_student_percents
        assert block.instructions == 'What are your favorite fruits?'
        assert block.num_inputs == 3
        assert block.num_top_words == 100

        node = etree.Element("unknown_root")
        # This will export the olx to a separate file.
        block.add_xml_to_node(node)
        with runtime.export_fs.open('word_cloud/block_id.xml') as f:
            exported_xml = f.read()

        assert exported_xml == original_xml
Example #10
0
 def get_usage_id(self, block_type, block_id):
     """
     Constructs usage id using 'block_type' and 'block_id'
     """
     return BlockUsageLocator(self.dummy_course_key,
                              block_type=block_type,
                              block_id=block_id)
    def test_replacement(self, key):
        course_key = CourseLocator('org',
                                   'course',
                                   'run',
                                   'rev',
                                   deprecated=True)
        kwargs = {key: 'newvalue'}
        self.assertEqual(
            getattr(
                BlockUsageLocator(course_key, 'c', 'n',
                                  deprecated=True).replace(**kwargs), key),
            'newvalue')

        with self.assertRaises(InvalidKeyError):
            BlockUsageLocator(course_key, 'c', 'n',
                              deprecated=True).replace(block_id=u'name\xae')
Example #12
0
 def setUp(self):
     super(PersistentSubsectionGradeTest, self).setUp()
     self.usage_key = BlockUsageLocator(
         course_key=self.course_key,
         block_type='subsection',
         block_id='subsection_12345',
     )
     self.block_records = BlockRecordList([self.record_a, self.record_b],
                                          self.course_key)
     self.params = {
         "user_id": 12345,
         "usage_key": self.usage_key,
         "course_version": "deadbeef",
         "subtree_edited_timestamp": "2016-08-01 18:53:24.354741Z",
         "earned_all": 6.0,
         "possible_all": 12.0,
         "earned_graded": 6.0,
         "possible_graded": 8.0,
         "visible_blocks": self.block_records,
         "first_attempted": datetime(2000,
                                     1,
                                     1,
                                     12,
                                     30,
                                     45,
                                     tzinfo=pytz.UTC),
     }
Example #13
0
    def test_remap_namespace_native_xblock(self):

        # Set the XBlock's location
        self.xblock.location = BlockUsageLocator(
            CourseLocator("org", "import", "run"), "category", "stubxblock")

        # Explicitly set the content and settings fields
        self.xblock.test_content_field = "Explicitly set"
        self.xblock.test_settings_field = "Explicitly set"
        self.xblock.save()

        # Move to different runtime w/ different course id
        target_location_namespace = CourseKey.from_string("org/course/run")
        new_version = _update_and_import_module(
            self.xblock,
            modulestore(),
            999,
            self.xblock.location.course_key,
            target_location_namespace,
            do_import_static=False)

        # Check the XBlock's location
        assert new_version.location.course_key == target_location_namespace

        # Check the values of the fields.
        # The content and settings fields should be preserved
        assert new_version.test_content_field == 'Explicitly set'
        assert new_version.test_settings_field == 'Explicitly set'

        # Expect that these fields are marked explicitly set
        assert 'test_content_field' in new_version.get_explicitly_set_fields_by_scope(
            scope=Scope.content)
        assert 'test_settings_field' in new_version.get_explicitly_set_fields_by_scope(
            scope=Scope.settings)
Example #14
0
    def setUp(self):
        super(BaseOutcomeTest, self).setUp()
        self.course_key = CourseLocator(org='some_org',
                                        course='some_course',
                                        run='some_run')
        self.usage_key = BlockUsageLocator(course_key=self.course_key,
                                           block_type='problem',
                                           block_id='block_id')
        self.user = UserFactory.create()
        self.consumer = LtiConsumer(consumer_name='Lti Consumer Name',
                                    consumer_key='consumer_key',
                                    consumer_secret='consumer_secret',
                                    instance_guid='tool_instance_guid')
        self.consumer.save()
        outcome = OutcomeService(
            lis_outcome_service_url='http://example.com/service_url',
            lti_consumer=self.consumer)
        outcome.save()
        self.assignment = GradedAssignment(
            user=self.user,
            course_key=self.course_key,
            usage_key=self.usage_key,
            outcome_service=outcome,
            lis_result_sourcedid='sourcedid',
            version_number=1,
        )
        self.assignment.save()

        self.send_score_update_mock = self.setup_patch(
            'lti_provider.outcomes.send_score_update', None)
Example #15
0
 def block_key_factory(self, block_id):
     """
     Returns a block key object for the given block_id.
     """
     return BlockUsageLocator(course_key=self.course_key,
                              block_type='course',
                              block_id=unicode(block_id))
Example #16
0
    def setUp(self):
        super(TestResetGrades, self).setUp()
        self.command = reset_grades.Command()

        self.user_ids = [user_id for user_id in range(self.num_users)]

        self.course_keys = []
        for course_index in range(self.num_courses):
            self.course_keys.append(
                CourseLocator(
                    org='some_org',
                    course='some_course',
                    run=unicode(course_index),
                ))

        self.subsection_keys_by_course = {}
        for course_key in self.course_keys:
            subsection_keys_in_course = []
            for subsection_index in range(self.num_subsections):
                subsection_keys_in_course.append(
                    BlockUsageLocator(
                        course_key=course_key,
                        block_type='sequential',
                        block_id=unicode(subsection_index),
                    ))
            self.subsection_keys_by_course[
                course_key] = subsection_keys_in_course
Example #17
0
    def test_migrate_published_info(self):
        """
        Tests that blocks that were storing published_date and published_by through CMSBlockMixin are loaded correctly
        """

        # Insert the test block directly into the module store
        location = BlockUsageLocator(CourseLocator('edX',
                                                   'migration',
                                                   '2012_Fall',
                                                   deprecated=True),
                                     'html',
                                     'test_html',
                                     deprecated=True)
        published_date = datetime(1970, 1, 1, tzinfo=UTC)
        published_by = 123
        self.draft_store._update_single_item(
            as_draft(location),
            {
                'definition.data': {},
                'metadata': {
                    # published_date was previously stored as a list of time components, not a datetime
                    'published_date': list(published_date.timetuple()),
                    'published_by': published_by,
                },
            },
            allow_not_found=True,
        )

        # Retrieve the block and verify its fields
        component = self.draft_store.get_item(location)
        assert component.published_on == published_date
        assert component.published_by == published_by
Example #18
0
 def test_usage_key_replace(self, key_class, attr_value):
     attr, value = attr_value
     key = key_class(
         BlockUsageLocator(CourseLocator('org', 'course', 'run'),
                           'block_type', 'block_id'), 'aside')
     new_key = key.replace(**{attr: value})
     self.assertEqual(getattr(new_key, attr), value)
Example #19
0
    def test_path_to_location_for_orphan_vertical(self, module_store):
        r"""
        Make sure that path_to_location works with a component having multiple vertical parents,
        from which one of them is orphan.

         course
            |
         chapter
           |
         vertical vertical
            \     /
              html
        """
        # Get a course with orphan modules
        course = self.create_course_with_orphans(module_store)

        # Fetch the required course components.
        vertical1 = self.store.get_item(BlockUsageLocator(course.id, 'vertical', 'Vertical1'))
        chapter1 = self.store.get_item(BlockUsageLocator(course.id, 'chapter', 'Chapter1'))
        orphan_vertical = self.store.get_item(BlockUsageLocator(course.id, 'vertical', 'OrphanVert'))
        multi_parent_html = self.store.get_item(BlockUsageLocator(course.id, 'html', 'multi_parent_html'))

        # Verify `OrphanVert` is an orphan
        self.assertIn(orphan_vertical.location, self.store.get_orphans(course.id))

        # Verify `multi_parent_html` is child of both `Vertical1` and `OrphanVert`
        self.assertIn(multi_parent_html.location, orphan_vertical.children)
        self.assertIn(multi_parent_html.location, vertical1.children)

        # HTML component has `vertical1` as its parent.
        html_parent = self.store.get_parent_location(multi_parent_html.location)
        self.assertNotEqual(six.text_type(html_parent), six.text_type(orphan_vertical.location))
        self.assertEqual(six.text_type(html_parent), six.text_type(vertical1.location))

        # Get path of the `multi_parent_html` & verify path_to_location returns a expected path
        path = path_to_location(self.store, multi_parent_html.location)
        expected_path = (
            course.id,
            chapter1.location.block_id,
            vertical1.location.block_id,
            multi_parent_html.location.block_id,
            "",
            path[-1]
        )
        self.assertIsNotNone(path)
        self.assertEqual(len(path), 6)
        self.assertEqual(path, expected_path)
Example #20
0
    def setUp(self):
        super(TestOrderCreation, self).setUp()
        # Set enforce_csrf_checks=True here because testing must still
        # work (webhooks are explicitly exempted from CSRF protection)
        self.client = Client(enforce_csrf_checks=True)

        conf = settings.WEBHOOK_SETTINGS['edx_shopify']

        # Calculate 3 SHA256 hashes over the payload, which the
        # webhook handler must verify and accept or reject: a correct
        # hash, a hash from the wrong (reversed) key, and a corrupted
        # hash containing an invalid base64 character.
        correct_hash = hmac.new(conf['api_key'], self.raw_payload,
                                hashlib.sha256)
        incorrect_hash = hmac.new(conf['api_key'][::-1], self.raw_payload,
                                  hashlib.sha256)
        self.correct_signature = base64.b64encode(correct_hash.digest())
        self.incorrect_signature = base64.b64encode(incorrect_hash.digest())
        self.corrupt_signature = "-%s" % base64.b64encode(
            correct_hash.digest())[1:]  # noqa: E501

        # Set up a mock course
        course_id_string = 'course-v1:org+course+run1'
        cl = CourseLocator.from_string(course_id_string)
        bul = BlockUsageLocator(cl, u'course', u'course')
        course = Mock()
        course.id = cl
        course.system = Mock()
        course.scope_ids = Mock()
        course.scope_id.user_id = None
        course.scope_ids.block_type = u'course'
        course.scope_ids.def_id = bul
        course.scope_ids.usage_id = bul
        course.location = bul
        course.display_name = u'Course - Run 1'

        self.course_id_string = course_id_string
        self.cl = cl
        self.course = course

        email_params = {
            'registration_url':
            u'https://localhost:8000/register',  # noqa: E501
            'course_about_url':
            u'https://localhost:8000/courses/course-v1:org+course+run1/about',  # noqa: E501
            'site_name':
            'localhost:8000',
            'course':
            course,
            'is_shib_course':
            None,
            'display_name':
            u'Course - Run 1',
            'auto_enroll':
            True,
            'course_url':
            u'https://localhost:8000/courses/course-v1:org+course+run1/'
        }  # noqa: E501
        self.email_params = email_params
Example #21
0
 def test_unicode_loads(self):
     """
     Test that getting items from the test_unicode course works
     """
     assert self.draft_store.get_item(
         BlockUsageLocator(CourseLocator('edX', 'test_unicode', '2012_Fall', deprecated=True),
                           'course', '2012_Fall', deprecated=True)) is not None
     # All items with ascii-only filenames should load properly.
     assert self.draft_store.get_item(
         BlockUsageLocator(CourseLocator('edX', 'test_unicode', '2012_Fall', deprecated=True),
                           'video', 'Welcome', deprecated=True)) is not None
     assert self.draft_store.get_item(
         BlockUsageLocator(CourseLocator('edX', 'test_unicode', '2012_Fall', deprecated=True),
                           'video', 'Welcome', deprecated=True)) is not None
     assert self.draft_store.get_item(
         BlockUsageLocator(CourseLocator('edX', 'test_unicode', '2012_Fall', deprecated=True),
                           'chapter', 'Overview', deprecated=True)) is not None
    def setUp(self):
        super().setUp()
        self.course_key = CourseLocator('org', 'course', str(uuid4()))
        self.usage_key = BlockUsageLocator(course_key=self.course_key,
                                           block_type='course',
                                           block_id='course')

        self.params = self._create_bsm_params()
    def test_find_one(self):
        assert_not_none(
            self.draft_store._find_one(
                BlockUsageLocator(CourseLocator('edX', 'toy', '2012_Fall', deprecated=True),
                                  'course', '2012_Fall', deprecated=True)),
        )

        assert_not_none(
            self.draft_store._find_one(
                BlockUsageLocator(CourseLocator('edX', 'simple', '2012_Fall', deprecated=True),
                                  'course', '2012_Fall', deprecated=True)),
        )

        assert_not_none(
            self.draft_store._find_one(BlockUsageLocator(CourseLocator('edX', 'toy', '2012_Fall', deprecated=True),
                                                         'video', 'Welcome', deprecated=True)),
        )
Example #24
0
 def to_block_locator(self):
     """
     Returns a BlockUsageLocator for this location.
     """
     return BlockUsageLocator(
         course_key=self.course_key.to_course_locator(),
         block_type=self.block_type,
         block_id=self.block_id)
Example #25
0
 def _gen_usage_key(self):
     return BlockUsageLocator(
         self.course_key,
         self._gen_block_type(),
         # We've seen at most 1000 blocks requested in a course, so we'll
         # generate at most that many different indexes.
         str(numpy.random.randint(0, 1000)),
     )
    def setUp(self):
        super(BlockStructureModelTestCase, self).setUp()
        self.course_key = CourseLocator('org', 'course', unicode(uuid4()))
        self.usage_key = BlockUsageLocator(course_key=self.course_key,
                                           block_type='course',
                                           block_id='course')

        self.params = self._create_bsm_params()
Example #27
0
 def setUp(self):
     super().setUp()
     self.annotatable = AnnotatableBlock(
         get_test_system(), DictFieldData({'data': self.sample_xml}),
         ScopeIds(
             None, None, None,
             BlockUsageLocator(CourseLocator('org', 'course', 'run'),
                               'category', 'name')))
Example #28
0
 def _block(self, block):
     """Return a UsageKey for the block ``block``."""
     course = block // 1000
     return BlockUsageLocator(
         self._course(course),
         self._block_type(block),
         'block{}'.format(block)
     )
    def setUp(self):
        super().setUp()
        self.environ = {'wsgi.url_scheme': 'http', 'REQUEST_METHOD': 'POST'}
        self.request_body_xml_template = textwrap.dedent("""
            <?xml version = "1.0" encoding = "UTF-8"?>
                <imsx_POXEnvelopeRequest xmlns = "{namespace}">
                  <imsx_POXHeader>
                    <imsx_POXRequestHeaderInfo>
                      <imsx_version>V1.0</imsx_version>
                      <imsx_messageIdentifier>{messageIdentifier}</imsx_messageIdentifier>
                    </imsx_POXRequestHeaderInfo>
                  </imsx_POXHeader>
                  <imsx_POXBody>
                    <{action}>
                      <resultRecord>
                        <sourcedGUID>
                          <sourcedId>{sourcedId}</sourcedId>
                        </sourcedGUID>
                        <result>
                          <resultScore>
                            <language>en-us</language>
                            <textString>{grade}</textString>
                          </resultScore>
                        </result>
                      </resultRecord>
                    </{action}>
                  </imsx_POXBody>
                </imsx_POXEnvelopeRequest>
            """)
        self.system = get_test_system()
        self.system.get_real_user = Mock()
        self.system.publish = Mock()
        self.system.rebind_noauth_module_to_user = Mock()
        self.user_id = self.system.anonymous_student_id

        self.xmodule = LTIBlock(
            self.system,
            DictFieldData({}),
            ScopeIds(None, None, None, BlockUsageLocator(self.system.course_id, 'lti', 'name'))
        )
        self.lti_id = self.xmodule.lti_id
        self.unquoted_resource_link_id = '{}-i4x-2-3-lti-31de800015cf4afb973356dbe81496df'.format(
            self.xmodule.runtime.hostname
        )

        sourced_id = ':'.join(parse.quote(i) for i in (self.lti_id, self.unquoted_resource_link_id, self.user_id))  # lint-amnesty, pylint: disable=line-too-long

        self.defaults = {
            'namespace': "http://www.imsglobal.org/services/ltiv1p1/xsd/imsoms_v1p0",
            'sourcedId': sourced_id,
            'action': 'replaceResultRequest',
            'grade': 0.5,
            'messageIdentifier': '528243ba5241b',
        }

        self.xmodule.due = None
        self.xmodule.graceperiod = None
Example #30
0
    def _create_item(self,
                     category,
                     name,
                     data,
                     metadata,
                     parent_category,
                     parent_name,
                     draft=True,
                     split=True):
        """
        Create the item of the given category and block id in split and old mongo, add it to the optional
        parent. The parent category is only needed because old mongo requires it for the id.

        Note: if draft = False, it will create the draft and then publish it; so, it will overwrite any
        existing draft for both the new item and the parent
        """
        location = self.old_course_key.make_usage_key(category, name)
        self.draft_mongo.create_item(self.user_id,
                                     location.course_key,
                                     location.block_type,
                                     block_id=location.block_id,
                                     definition_data=data,
                                     metadata=metadata,
                                     runtime=self.runtime)
        if not draft:
            self.draft_mongo.publish(location, self.user_id)
        if isinstance(data, basestring):
            fields = {'data': data}
        else:
            fields = data.copy()
        fields.update(metadata)
        if parent_name:
            # add child to parent in mongo
            parent_location = self.old_course_key.make_usage_key(
                parent_category, parent_name)
            parent = self.draft_mongo.get_item(parent_location)
            parent.children.append(location)
            self.draft_mongo.update_item(parent, self.user_id)
            if not draft:
                self.draft_mongo.publish(parent_location, self.user_id)
            # create child for split
            if split:
                self.split_mongo.create_child(
                    self.user_id,
                    BlockUsageLocator(course_key=self.split_course_key,
                                      block_type=parent_category,
                                      block_id=parent_name),
                    category,
                    block_id=name,
                    fields=fields)
        else:
            if split:
                self.split_mongo.create_item(self.user_id,
                                             self.split_course_key,
                                             category,
                                             block_id=name,
                                             fields=fields)