diff --git a/exporter/api.py b/exporter/api.py
index 2d784156..f1293e45 100644
--- a/exporter/api.py
+++ b/exporter/api.py
@@ -4,11 +4,11 @@
from __future__ import unicode_literals
-from tempfile import mkdtemp, mkstemp
-import tarfile
import os
-from shutil import rmtree
import re
+import tarfile
+from shutil import rmtree
+from tempfile import mkdtemp, mkstemp
from django.utils.text import slugify
from django.core.files.storage import default_storage
diff --git a/rest/tests/base.py b/rest/tests/base.py
index 86287218..ac2fcc17 100644
--- a/rest/tests/base.py
+++ b/rest/tests/base.py
@@ -759,7 +759,7 @@ def get_learning_resource_export_task(self, repo_slug, task_id,
if expected_status == HTTP_200_OK:
return as_json(resp)
- def create_learning_resource_export_task(self, repo_slug,
+ def create_learning_resource_export_task(self, repo_slug, input_dict,
expected_status=HTTP_201_CREATED):
"""
Helper method to create a task for the user to export a tarball of
@@ -769,7 +769,9 @@ def create_learning_resource_export_task(self, repo_slug,
'/api/v1/repositories/{repo_slug}/'
'learning_resource_export_tasks/'.format(
repo_slug=repo_slug,
- )
+ ),
+ json.dumps(input_dict),
+ content_type='application/json',
)
self.assertEqual(expected_status, resp.status_code)
if expected_status == HTTP_201_CREATED:
diff --git a/rest/tests/test_export.py b/rest/tests/test_export.py
index e6526e32..c7828d4a 100644
--- a/rest/tests/test_export.py
+++ b/rest/tests/test_export.py
@@ -6,7 +6,9 @@
from tempfile import mkdtemp
from shutil import rmtree
import tarfile
+import os
+from django.utils.text import slugify
from django.test import override_settings
from rest_framework.status import HTTP_200_OK, HTTP_404_NOT_FOUND
from six import BytesIO
@@ -28,6 +30,7 @@ class TestExport(RESTTestCase):
)
def test_create_new_task(self):
"""Test a basic export."""
+ self.import_course_tarball(self.repo)
resources = LearningResource.objects.filter(
course__repository__id=self.repo.id).all()
for resource in resources:
@@ -35,8 +38,11 @@ def test_create_new_task(self):
"id": resource.id
})
+ # Skip first one to test that it's excluded from export.
task_id = self.create_learning_resource_export_task(
- self.repo.slug)['id']
+ self.repo.slug,
+ {"ids": [r.id for r in resources[1:]]}
+ )['id']
result = self.get_learning_resource_export_tasks(
self.repo.slug)['results'][0]
@@ -61,11 +67,23 @@ def test_create_new_task(self):
self.assertEqual(HTTP_200_OK, resp.status_code)
tempdir = mkdtemp()
+
+ def make_path(resource):
+ """Create a path that should exist for a resource."""
+ type_name = resource.learning_resource_type.name
+ return os.path.join(
+ tempdir, type_name, "{id}_{url_name}.xml".format(
+ id=resource.id,
+ url_name=slugify(resource.url_name)[:200],
+ )
+ )
try:
fakefile = BytesIO(b"".join(resp.streaming_content))
with tarfile.open(fileobj=fakefile, mode="r:gz") as tar:
tar.extractall(path=tempdir)
- assert_resource_directory(self, resources, tempdir)
+ self.assertFalse(os.path.isfile(make_path(resources[0])))
+ assert_resource_directory(self, resources[1:], tempdir)
+
finally:
rmtree(tempdir)
diff --git a/rest/views.py b/rest/views.py
index 0406428b..cf8c618a 100644
--- a/rest/views.py
+++ b/rest/views.py
@@ -678,9 +678,16 @@ def post(self, request, *args, **kwargs):
repo_slug = self.kwargs['repo_slug']
try:
- exports = self.request.session[EXPORTS_KEY][repo_slug]
+ exports = set(self.request.session[EXPORTS_KEY][repo_slug])
except KeyError:
- exports = []
+ exports = set()
+
+ ids = self.request.data['ids']
+ for resource_id in ids:
+ if resource_id not in exports:
+ raise ValidationError("id {id} is not in export list".format(
+ id=resource_id
+ ))
# Cancel any old tasks.
old_tasks = self.request.session.get(
@@ -694,7 +701,7 @@ def post(self, request, *args, **kwargs):
self.request.session[EXPORT_TASK_KEY][repo_slug] = {}
learning_resources = LearningResource.objects.filter(
- id__in=exports).all()
+ id__in=ids).all()
result = export_resources.delay(
learning_resources, self.request.user.username)
diff --git a/ui/jstests/test_lr_exports.jsx b/ui/jstests/test_lr_exports.jsx
index f7ff0594..f194c959 100644
--- a/ui/jstests/test_lr_exports.jsx
+++ b/ui/jstests/test_lr_exports.jsx
@@ -58,7 +58,8 @@ define(['QUnit', 'jquery', 'lr_exports', 'reactaddons',
{
"id": "task1",
"status": "successful",
- "url": "/media/resource_exports/export_task1.tar.gz"
+ "url": "/media/resource_exports/export_task1.tar.gz",
+ "collision": false
}
]
}
@@ -76,7 +77,8 @@ define(['QUnit', 'jquery', 'lr_exports', 'reactaddons',
responseText: {
"id": "task2",
"status": "processing",
- "url": ""
+ "url": "",
+ "collision": false
}
});
TestUtils.initMockjax({
@@ -107,7 +109,9 @@ define(['QUnit', 'jquery', 'lr_exports', 'reactaddons',
assert.deepEqual(
{
exportButtonVisible: false,
- exports: []
+ exports: [],
+ exportsSelected: [],
+ collision: false
},
component.state
);
@@ -118,7 +122,9 @@ define(['QUnit', 'jquery', 'lr_exports', 'reactaddons',
assert.deepEqual(
{
exports: [learningResourceResponse],
- exportButtonVisible: true
+ exportButtonVisible: true,
+ exportsSelected: [learningResourceResponse.id],
+ collision: false
},
component.state
);
@@ -133,7 +139,9 @@ define(['QUnit', 'jquery', 'lr_exports', 'reactaddons',
assert.deepEqual(
{
exports: [learningResourceResponse],
- exportButtonVisible: false
+ exportButtonVisible: false,
+ exportsSelected: [learningResourceResponse.id],
+ collision: false
},
component.state
);
@@ -145,7 +153,8 @@ define(['QUnit', 'jquery', 'lr_exports', 'reactaddons',
responseText: {
"id": "task2",
"status": "processing",
- "url": ""
+ "url": "",
+ "collision": false
}
});
@@ -154,7 +163,9 @@ define(['QUnit', 'jquery', 'lr_exports', 'reactaddons',
assert.deepEqual(
{
exportButtonVisible: false,
- exports: [learningResourceResponse]
+ exports: [learningResourceResponse],
+ exportsSelected: [learningResourceResponse.id],
+ collision: false
},
component.state
);
@@ -166,7 +177,8 @@ define(['QUnit', 'jquery', 'lr_exports', 'reactaddons',
responseText: {
"id": "task2",
"status": "success",
- "url": "/media/resource_exports/export_task2.tar.gz"
+ "url": "/media/resource_exports/export_task2.tar.gz",
+ "collision": false
}
});
@@ -179,6 +191,8 @@ define(['QUnit', 'jquery', 'lr_exports', 'reactaddons',
{
exports: [],
exportButtonVisible: false,
+ exportsSelected: [],
+ collision: false,
url: "/media/resource_exports/export_task2.tar.gz"
},
component.state
@@ -221,7 +235,9 @@ define(['QUnit', 'jquery', 'lr_exports', 'reactaddons',
assert.deepEqual(
{
exportButtonVisible: false,
- exports: []
+ exports: [],
+ exportsSelected: [],
+ collision: false
},
component.state
);
@@ -231,7 +247,9 @@ define(['QUnit', 'jquery', 'lr_exports', 'reactaddons',
assert.deepEqual(
{
exports: [],
- exportButtonVisible: false
+ exportButtonVisible: false,
+ exportsSelected: [],
+ collision: false
},
component.state
);
@@ -257,7 +275,9 @@ define(['QUnit', 'jquery', 'lr_exports', 'reactaddons',
assert.deepEqual(
{
exportButtonVisible: false,
- exports: []
+ exports: [],
+ exportsSelected: [],
+ collision: false
},
component.state
);
@@ -268,7 +288,9 @@ define(['QUnit', 'jquery', 'lr_exports', 'reactaddons',
assert.deepEqual(
{
exports: [learningResourceResponse],
- exportButtonVisible: true
+ exportButtonVisible: true,
+ exportsSelected: [learningResourceResponse.id],
+ collision: false
},
component.state
);
@@ -291,6 +313,8 @@ define(['QUnit', 'jquery', 'lr_exports', 'reactaddons',
{
exports: [learningResourceResponse],
exportButtonVisible: true,
+ exportsSelected: [learningResourceResponse.id],
+ collision: false,
message: {
error: "Error preparing learning resources for download."
}
@@ -321,7 +345,9 @@ define(['QUnit', 'jquery', 'lr_exports', 'reactaddons',
assert.deepEqual(
{
exportButtonVisible: false,
- exports: []
+ exports: [],
+ exportsSelected: [],
+ collision: false
},
component.state
);
@@ -332,7 +358,9 @@ define(['QUnit', 'jquery', 'lr_exports', 'reactaddons',
assert.deepEqual(
{
exports: [learningResourceResponse],
- exportButtonVisible: true
+ exportButtonVisible: true,
+ exportsSelected: [learningResourceResponse.id],
+ collision: false
},
component.state
);
@@ -355,6 +383,8 @@ define(['QUnit', 'jquery', 'lr_exports', 'reactaddons',
{
exports: [learningResourceResponse],
exportButtonVisible: false,
+ exportsSelected: [learningResourceResponse.id],
+ collision: false,
message: {
error: "Error occurred preparing " +
"learning resources for download."
@@ -387,7 +417,9 @@ define(['QUnit', 'jquery', 'lr_exports', 'reactaddons',
assert.deepEqual(
{
exportButtonVisible: false,
- exports: []
+ exports: [],
+ exportsSelected: [],
+ collision: false
},
component.state
);
@@ -398,7 +430,9 @@ define(['QUnit', 'jquery', 'lr_exports', 'reactaddons',
assert.deepEqual(
{
exports: [learningResourceResponse],
- exportButtonVisible: true
+ exportButtonVisible: true,
+ exportsSelected: [learningResourceResponse.id],
+ collision: false
},
component.state
);
@@ -413,7 +447,9 @@ define(['QUnit', 'jquery', 'lr_exports', 'reactaddons',
assert.deepEqual(
{
exports: [learningResourceResponse],
- exportButtonVisible: false
+ exportButtonVisible: false,
+ exportsSelected: [learningResourceResponse.id],
+ collision: false
},
component.state
);
@@ -432,6 +468,8 @@ define(['QUnit', 'jquery', 'lr_exports', 'reactaddons',
{
exports: [learningResourceResponse],
exportButtonVisible: false,
+ exportsSelected: [learningResourceResponse.id],
+ collision: false,
message: {
error: "Error occurred preparing learning " +
"resources for download."
@@ -465,7 +503,9 @@ define(['QUnit', 'jquery', 'lr_exports', 'reactaddons',
assert.deepEqual(
{
exportButtonVisible: false,
- exports: []
+ exports: [],
+ exportsSelected: [],
+ collision: false
},
component.state
);
@@ -476,7 +516,9 @@ define(['QUnit', 'jquery', 'lr_exports', 'reactaddons',
assert.deepEqual(
{
exports: [learningResourceResponse],
- exportButtonVisible: true
+ exportButtonVisible: true,
+ exportsSelected: [learningResourceResponse.id],
+ collision: false
},
component.state
);
@@ -491,7 +533,9 @@ define(['QUnit', 'jquery', 'lr_exports', 'reactaddons',
assert.deepEqual(
{
exports: [learningResourceResponse],
- exportButtonVisible: false
+ exportButtonVisible: false,
+ exportsSelected: [learningResourceResponse.id],
+ collision: false
},
component.state
);
@@ -503,7 +547,8 @@ define(['QUnit', 'jquery', 'lr_exports', 'reactaddons',
responseText: {
"id": "task2",
"status": "processing",
- "url": ""
+ "url": "",
+ "collision": false
}
});
@@ -513,7 +558,9 @@ define(['QUnit', 'jquery', 'lr_exports', 'reactaddons',
assert.deepEqual(
{
exportButtonVisible: false,
- exports: [learningResourceResponse]
+ exports: [learningResourceResponse],
+ exportsSelected: [learningResourceResponse.id],
+ collision: false
},
component.state
);
@@ -522,7 +569,12 @@ define(['QUnit', 'jquery', 'lr_exports', 'reactaddons',
url: '/api/v1/repositories/repo/' +
'learning_resource_export_tasks/task2/',
type: 'GET',
- status: 400
+ responseText: {
+ "id": "task2",
+ "status": "failure",
+ "url": "",
+ "collision": false
+ }
});
waitForAjax(1, function() {
@@ -530,6 +582,117 @@ define(['QUnit', 'jquery', 'lr_exports', 'reactaddons',
{
exports: [learningResourceResponse],
exportButtonVisible: false,
+ exportsSelected: [learningResourceResponse.id],
+ message: {
+ error: "Error occurred preparing learning " +
+ "resources for download."
+ },
+ collision: false
+ },
+ component.state
+ );
+ done();
+ });
+ });
+ });
+ });
+ };
+
+ React.addons.TestUtils.renderIntoDocument(
+