Exemple #1
0
 def test_new_resource(self):
     """
     When a new LearningResource is created, it will have no
     terms assigned to it.
     """
     resource = create_resource(
         course=self.course,
         parent=None,
         resource_type="problem",
         title="newly created",
         content_xml="<xml/>",
         mpath="",
         url_name="",
         dpath="",
     )
     terms = resource.terms.all()
     self.assertEqual(terms.count(), 0)
Exemple #2
0
 def create_resource(self, **kwargs):
     """Creates a learning resource with extra fields"""
     learn_res = create_resource(
         course=self.course,
         parent=kwargs.get('parent'),
         resource_type=kwargs.get('resource_type', "example"),
         title=kwargs.get('title', "other silly example"),
         content_xml=kwargs.get('content_xml', "<blah>other blah</blah>"),
         mpath=kwargs.get('mpath', "/otherblah"),
         url_name=kwargs.get('url_name'),
         dpath='')
     learn_res.xa_nr_views = kwargs.get('xa_nr_views', 0)
     learn_res.xa_nr_attempts = kwargs.get('xa_nr_attempts', 0)
     learn_res.xa_avg_grade = kwargs.get('xa_avg_grade', 0)
     learn_res.save()
     update_description_path(learn_res)
     return learn_res
Exemple #3
0
 def create_resource(self, **kwargs):
     """Creates a learning resource with extra fields"""
     learn_res = create_resource(
         course=self.course,
         parent=kwargs.get('parent'),
         resource_type=kwargs.get('resource_type', "example"),
         title=kwargs.get('title', "other silly example"),
         content_xml=kwargs.get('content_xml', "<blah>other blah</blah>"),
         mpath=kwargs.get('mpath', "/otherblah"),
         url_name=kwargs.get('url_name'),
         dpath=''
     )
     learn_res.xa_nr_views = kwargs.get('xa_nr_views', 0)
     learn_res.xa_nr_attempts = kwargs.get('xa_nr_attempts', 0)
     learn_res.xa_avg_grade = kwargs.get('xa_avg_grade', 0)
     learn_res.save()
     update_description_path(learn_res)
     return learn_res
Exemple #4
0
def import_children(course, element, parent):
    """
    Create LearningResource instances for each element
    of an XML tree.

    Args:
        course (learningresources.Course): Course
        element (lxml.etree): XML element within xbundle
        parent (learningresources.LearningResource): parent LearningResource
    """
    mpath = etree.ElementTree(element).getpath(element)
    resource = create_resource(
        course=course, parent=parent, resource_type=element.tag,
        title=element.attrib.get("display_name", "MISSING"),
        content_xml=etree.tostring(element),
        mpath=mpath,
    )
    for child in element.getchildren():
        if child.tag in DESCRIPTOR_TAGS:
            import_children(course, child, resource)
Exemple #5
0
    def setUp(self):
        super(TestExport, self).setUp()

        # Add some LearningResources on top of the default to make things
        # interesting.
        tarball_file = self.get_course_single_tarball()
        import_file(tarball_file, self.repo.id, self.user.id)

        # Add a resource with a '/' in the title and too many characters.
        course = self.repo.course_set.first()
        resource = create_resource(
            course=course,
            resource_type=LearningResourceType.objects.first().name,
            title="//x"*300,
            content_xml="",
            mpath="",
            url_name=None,
            parent=None,
            dpath=''
        )
        update_description_path(resource)

        # Add static assets.
        with TemporaryFile() as temp1:
            with TemporaryFile() as temp2:
                temp1.write(b"file1")
                temp2.write(b"file2")
                file1 = File(temp1, name="iamafile1.txt")
                file2 = File(temp2, name="iamafile2.txt")

                asset1 = create_static_asset(course.id, file1)
                resource.static_assets.add(asset1)

                # If url_name is missing we should just use id in filename.
                resource.url_name = None
                resource.save()

                asset2 = create_static_asset(course.id, file2)
                self.resource.static_assets.add(asset2)
Exemple #6
0
    def setUp(self):
        super(TestExport, self).setUp()

        # Add some LearningResources on top of the default to make things
        # interesting.
        tarball_file = self.get_course_single_tarball()
        import_file(tarball_file, self.repo.id, self.user.id)

        # Add a resource with a '/' in the title and too many characters.
        course = self.repo.course_set.first()
        resource = create_resource(
            course=course,
            resource_type=LearningResourceType.objects.first().name,
            title="//x" * 300,
            content_xml="",
            mpath="",
            url_name=None,
            parent=None,
            dpath='')
        update_description_path(resource)

        # Add static assets.
        with TemporaryFile() as temp1:
            with TemporaryFile() as temp2:
                temp1.write(b"file1")
                temp2.write(b"file2")
                file1 = File(temp1, name="iamafile1.txt")
                file2 = File(temp2, name="iamafile2.txt")

                asset1 = create_static_asset(course.id, file1)
                resource.static_assets.add(asset1)

                # If url_name is missing we should just use id in filename.
                resource.url_name = None
                resource.save()

                asset2 = create_static_asset(course.id, file2)
                self.resource.static_assets.add(asset2)
Exemple #7
0
    def test_duplicate(self):
        """
        Test that exporting resources with same file path
        is reported and handled correctly.
        """

        with TemporaryFile() as temp1:
            with TemporaryFile() as temp2:
                temp1.write(b"first")
                temp2.write(b"second")

                # Create two static assets with the same name but different
                # paths so no renaming is done on storage side.
                static_filename = "iamafile.txt"
                static_path1 = os.path.join(
                    STATIC_ASSET_BASEPATH.format(
                        org=self.resource.course.org,
                        course_number=self.resource.course.course_number,
                        run=self.resource.course.run,
                    ),
                    static_filename
                )

                file1 = File(temp1, name=static_filename)
                file2 = File(temp2, name=static_filename)

                default_storage.delete(static_path1)
                asset1 = create_static_asset(self.resource.course.id, file1)
                self.resource.static_assets.add(asset1)

                course2 = create_course(
                    "org2", self.repo.id, self.resource.course.course_number,
                    self.resource.course.run, self.user.id
                )
                resource2 = create_resource(
                    course=course2,
                    parent=None,
                    resource_type=self.resource.learning_resource_type.name,
                    title=self.resource.title,
                    dpath="",
                    mpath="",
                    content_xml="",
                    url_name=self.resource.url_name
                )
                static_path2 = os.path.join(
                    STATIC_ASSET_BASEPATH.format(
                        org=course2.org,
                        course_number=course2.course_number,
                        run=course2.run,
                    ),
                    static_filename
                )
                default_storage.delete(static_path2)
                asset2 = create_static_asset(resource2.course.id, file2)
                resource2.static_assets.add(asset2)

                # Export the resources. The second static asset should have
                # the number _1 attached to it.
                try:

                    resources = [self.resource, resource2]
                    tempdir, collision = export_resources_to_directory(
                        resources)
                    try:
                        self.assertTrue(collision)
                        assert_resource_directory(self, resources, tempdir)
                    finally:
                        rmtree(tempdir)
                finally:
                    default_storage.delete(asset1.asset.name)
                    default_storage.delete(asset2.asset.name)
Exemple #8
0
def import_children(course, element, parent, parent_dpath):
    """
    Create LearningResource instances for each element
    of an XML tree.

    Args:
        course (learningresources.models.Course): Course
        element (lxml.etree): XML element within xbundle
        parent (learningresources.models.LearningResource):
            Parent LearningResource
        parent_dpath (unicode): parent description path
    Returns:
        None
    """
    # pylint: disable=too-many-locals
    title = element.attrib.get("display_name", "MISSING")
    mpath = etree.ElementTree(element).getpath(element)
    dpath = join_description_paths(parent_dpath, title)
    resource = create_resource(
        course=course, parent=parent, resource_type=element.tag,
        title=title,
        content_xml=etree.tostring(element),
        mpath=mpath,
        url_name=element.attrib.get("url_name", None),
        dpath=dpath,
    )
    target = "/static/"
    if element.tag == "video":
        subname = get_video_sub(element)
        if subname != "":
            assets = StaticAsset.objects.filter(
                course__id=resource.course_id,
                asset=course_asset_basepath(course, subname),
            )
            for asset in assets:
                resource.static_assets.add(asset)
    else:
        # Recursively find all sub-elements, looking for anything which
        # refers to /static/. Then make the association between the
        # LearningResource and StaticAsset if the StaticAsset exists.
        # This is like doing soup.findAll("a") and checking for whether
        # "/static/" is in the href, which would work but also requires
        # more code to check for link, img, iframe, script, and others,
        # and within those, check for href or src existing.
        soup = BeautifulSoup(etree.tostring(element), 'lxml')
        for child in soup.findAll():
            for _, val in child.attrs.items():
                try:
                    if val.startswith(target):
                        path = val[len(target):]
                        try:
                            asset = StaticAsset.objects.get(
                                course__id=resource.course_id,
                                asset=course_asset_basepath(course, path),
                            )
                            resource.static_assets.add(asset)
                        except StaticAsset.DoesNotExist:
                            continue
                except AttributeError:
                    continue  # not a string

    for child in element.getchildren():
        if child.tag in DESCRIPTOR_TAGS:
            import_children(course, child, resource, dpath)
Exemple #9
0
def import_children(course, element, parent, parent_dpath):
    """
    Create LearningResource instances for each element
    of an XML tree.

    Args:
        course (learningresources.models.Course): Course
        element (lxml.etree): XML element within xbundle
        parent (learningresources.models.LearningResource):
            Parent LearningResource
        parent_dpath (unicode): parent description path
    Returns:
        None
    """
    # pylint: disable=too-many-locals
    title = element.attrib.get("display_name", MissingTitle.for_title_field)
    desc_path = title
    if desc_path == MissingTitle.for_title_field:
        desc_path = MissingTitle.for_desc_path_field
    mpath = etree.ElementTree(element).getpath(element)
    dpath = join_description_paths(parent_dpath, desc_path)
    resource = create_resource(
        course=course,
        parent=parent,
        resource_type=element.tag,
        title=title,
        content_xml=etree.tostring(element),
        mpath=mpath,
        url_name=element.attrib.get("url_name", None),
        dpath=dpath,
    )
    # temp variable to store static assets for bulk insert
    static_assets_to_save = set()
    target = "/static/"
    if element.tag == "video":
        subname = get_video_sub(element)
        if subname != "":
            assets = StaticAsset.objects.filter(
                course__id=resource.course_id,
                asset=course_asset_basepath(course, subname),
            )
            for asset in assets:
                static_assets_to_save.add((resource, asset))
    else:
        # Recursively find all sub-elements, looking for anything which
        # refers to /static/. Then make the association between the
        # LearningResource and StaticAsset if the StaticAsset exists.
        # This is like doing soup.findAll("a") and checking for whether
        # "/static/" is in the href, which would work but also requires
        # more code to check for link, img, iframe, script, and others,
        # and within those, check for href or src existing.
        soup = BeautifulSoup(etree.tostring(element), 'lxml')
        for child in soup.findAll():
            for _, val in child.attrs.items():
                try:
                    if val.startswith(target):
                        path = val[len(target):]
                        try:
                            asset = StaticAsset.objects.get(
                                course__id=resource.course_id,
                                asset=course_asset_basepath(course, path),
                            )
                            static_assets_to_save.add((resource, asset))
                        except StaticAsset.DoesNotExist:
                            continue
                except AttributeError:
                    continue  # not a string
    # Bulk insert of static assets
    # Using this approach to avoid signals during the learning resource .save()
    # Each signal triggers a reindex of the learning resource that is useless
    # during import because all the learning resources are indexed in bulk at
    # the end of the import anyway
    ThroughModel = LearningResource.static_assets.through
    ThroughModel.objects.bulk_create([
        ThroughModel(learningresource_id=resource.id, staticasset_id=asset.id)
        for resource, asset in static_assets_to_save
    ])

    for child in element.getchildren():
        if child.tag in DESCRIPTOR_TAGS:
            import_children(course, child, resource, dpath)
Exemple #10
0
def import_children(course, element, parent, parent_dpath):
    """
    Create LearningResource instances for each element
    of an XML tree.

    Args:
        course (learningresources.models.Course): Course
        element (lxml.etree): XML element within xbundle
        parent (learningresources.models.LearningResource):
            Parent LearningResource
        parent_dpath (unicode): parent description path
    Returns:
        None
    """
    # pylint: disable=too-many-locals
    title = element.attrib.get(
        "display_name", MissingTitle.for_title_field)
    desc_path = title
    if desc_path == MissingTitle.for_title_field:
        desc_path = MissingTitle.for_desc_path_field
    mpath = etree.ElementTree(element).getpath(element)
    dpath = join_description_paths(parent_dpath, desc_path)
    resource = create_resource(
        course=course, parent=parent, resource_type=element.tag,
        title=title,
        content_xml=etree.tostring(element),
        mpath=mpath,
        url_name=element.attrib.get("url_name", None),
        dpath=dpath,
    )
    # temp variable to store static assets for bulk insert
    static_assets_to_save = set()
    target = "/static/"
    if element.tag == "video":
        subname = get_video_sub(element)
        if subname != "":
            assets = StaticAsset.objects.filter(
                course__id=resource.course_id,
                asset=course_asset_basepath(course, subname),
            )
            for asset in assets:
                static_assets_to_save.add((resource, asset))
    else:
        # Recursively find all sub-elements, looking for anything which
        # refers to /static/. Then make the association between the
        # LearningResource and StaticAsset if the StaticAsset exists.
        # This is like doing soup.findAll("a") and checking for whether
        # "/static/" is in the href, which would work but also requires
        # more code to check for link, img, iframe, script, and others,
        # and within those, check for href or src existing.
        soup = BeautifulSoup(etree.tostring(element), 'lxml')
        for child in soup.findAll():
            for _, val in child.attrs.items():
                try:
                    if val.startswith(target):
                        path = val[len(target):]
                        try:
                            asset = StaticAsset.objects.get(
                                course__id=resource.course_id,
                                asset=course_asset_basepath(course, path),
                            )
                            static_assets_to_save.add((resource, asset))
                        except StaticAsset.DoesNotExist:
                            continue
                except AttributeError:
                    continue  # not a string
    # Bulk insert of static assets
    # Using this approach to avoid signals during the learning resource .save()
    # Each signal triggers a reindex of the learning resource that is useless
    # during import because all the learning resources are indexed in bulk at
    # the end of the import anyway
    ThroughModel = LearningResource.static_assets.through
    ThroughModel.objects.bulk_create(
        [
            ThroughModel(
                learningresource_id=resource.id,
                staticasset_id=asset.id
            )
            for resource, asset in static_assets_to_save
        ]
    )

    for child in element.getchildren():
        if child.tag in DESCRIPTOR_TAGS:
            import_children(course, child, resource, dpath)
Exemple #11
0
    def test_duplicate(self):
        """
        Test that exporting resources with same file path
        is reported and handled correctly.
        """

        with TemporaryFile() as temp1:
            with TemporaryFile() as temp2:
                temp1.write(b"first")
                temp2.write(b"second")

                # Create two static assets with the same name but different
                # paths so no renaming is done on storage side.
                static_filename = "iamafile.txt"
                static_path1 = os.path.join(
                    STATIC_ASSET_BASEPATH.format(
                        org=self.resource.course.org,
                        course_number=self.resource.course.course_number,
                        run=self.resource.course.run,
                    ), static_filename)

                file1 = File(temp1, name=static_filename)
                file2 = File(temp2, name=static_filename)

                default_storage.delete(static_path1)
                asset1 = create_static_asset(self.resource.course.id, file1)
                self.resource.static_assets.add(asset1)

                course2 = create_course("org2", self.repo.id,
                                        self.resource.course.course_number,
                                        self.resource.course.run, self.user.id)
                resource2 = create_resource(
                    course=course2,
                    parent=None,
                    resource_type=self.resource.learning_resource_type.name,
                    title=self.resource.title,
                    dpath="",
                    mpath="",
                    content_xml="",
                    url_name=self.resource.url_name)
                static_path2 = os.path.join(
                    STATIC_ASSET_BASEPATH.format(
                        org=course2.org,
                        course_number=course2.course_number,
                        run=course2.run,
                    ), static_filename)
                default_storage.delete(static_path2)
                asset2 = create_static_asset(resource2.course.id, file2)
                resource2.static_assets.add(asset2)

                # Export the resources. The second static asset should have
                # the number _1 attached to it.
                try:

                    resources = [self.resource, resource2]
                    tempdir, collision = export_resources_to_directory(
                        resources)
                    try:
                        self.assertTrue(collision)
                        assert_resource_directory(self, resources, tempdir)
                    finally:
                        rmtree(tempdir)
                finally:
                    default_storage.delete(asset1.asset.name)
                    default_storage.delete(asset2.asset.name)