Repository: learningequality/kolibri

Issue:
Deleted content after canceling content import
### Observed behavior

Currently, when we import content from a channel and try to cancel the process, the content will be deleted in the local data folder.

### Expected behavior

@jamalex suggests that it could be better if we keep the content instead of deleting it even after we cancel the import process. In this case, we will probably need to skip the check for cancellation in `importcontent.py` but directly go to annotate downloaded files.

### User-facing consequences

It will be more flexible for the users, since they are able to choose whether they want the downloaded content or not. 

### Errors and logs

Relevant errors and tracebacks from:

* the command line
* `~/.kolibri/kolibri.log`
* the browser console

```
server logs
```

```
browser console logs
```

### Steps to reproduce

precise steps that someone else can follow in order to see this behavior

### Context

* Kolibri version: ?
* Operating system: ?
* Browser: ?



Discussion:

Resolving patch:
diff --git a/kolibri/content/management/commands/importcontent.py b/kolibri/content/management/commands/importcontent.py
--- a/kolibri/content/management/commands/importcontent.py
+++ b/kolibri/content/management/commands/importcontent.py
@@ -5,7 +5,7 @@
 from kolibri.tasks.management.commands.base import AsyncCommand
 from requests.exceptions import HTTPError
 
-from ...utils import annotation, paths, transfer, import_export_content
+from ...utils import annotation, import_export_content, paths, transfer
 
 # constants to specify the transfer method to be used
 DOWNLOAD_METHOD = "download"
@@ -108,7 +108,6 @@ def _transfer(self, method, channel_id, path=None, node_ids=None, exclude_node_i
         files_to_download, total_bytes_to_transfer = import_export_content.get_files_to_transfer(
             channel_id, node_ids, exclude_node_ids, False)
 
-        downloaded_files = []
         number_of_skipped_files = 0
         file_checksums_to_annotate = []
 
@@ -149,9 +148,6 @@ def _transfer(self, method, channel_id, path=None, node_ids=None, exclude_node_i
                                 length = len(chunk)
                                 overall_progress_update(length)
                                 file_dl_progress_update(length)
-                            else:
-                                # If the for loop didn't break, add this to downloaded files.
-                                downloaded_files.append(dest)
 
                     file_checksums_to_annotate.append(f.id)
 
@@ -162,17 +158,15 @@ def _transfer(self, method, channel_id, path=None, node_ids=None, exclude_node_i
                     number_of_skipped_files += 1
                     overall_progress_update(f.file_size)
 
+            annotation.set_availability(channel_id, file_checksums_to_annotate)
+
+            if number_of_skipped_files > 0:
+                logging.warning(
+                    "{} files are skipped, because they are not found in the given external drive.".format(
+                        number_of_skipped_files))
+
             if self.is_cancelled():
-                # Cancelled, clean up any already downloading files.
-                for dest in downloaded_files:
-                    os.remove(dest)
                 self.cancel()
-            else:
-                if number_of_skipped_files > 0:
-                    logging.warning(
-                        "{} files are skipped, because they are not found in the given external drive.".format(number_of_skipped_files))
-
-                annotation.set_availability(channel_id, file_checksums_to_annotate)
 
     def handle_async(self, *args, **options):
         if options['command'] == 'network':


Test patch:
diff --git a/kolibri/content/test/test_import_export.py b/kolibri/content/test/test_import_export.py
--- a/kolibri/content/test/test_import_export.py
+++ b/kolibri/content/test/test_import_export.py
@@ -127,8 +127,8 @@ def test_remote_cancel_during_transfer(self, is_cancelled_mock, cancel_mock, Fil
     @patch('kolibri.content.management.commands.importcontent.transfer.FileDownload')
     @patch('kolibri.content.management.commands.importcontent.AsyncCommand.cancel')
     @patch('kolibri.content.management.commands.importcontent.AsyncCommand.is_cancelled', side_effect=[False, False, False, False, False, True, True, True])
-    def test_remote_cancel_after_file_copy_file_deleted(self, is_cancelled_mock, cancel_mock, FileDownloadMock, local_path_mock, remote_path_mock,
-                                                        start_progress_mock, annotation_mock):
+    def test_remote_cancel_after_file_copy_file_not_deleted(self, is_cancelled_mock, cancel_mock, FileDownloadMock, local_path_mock, remote_path_mock,
+                                                            start_progress_mock, annotation_mock):
         # If transfer is cancelled after transfer of first file
         local_path_1 = tempfile.mkstemp()[1]
         local_path_2 = tempfile.mkstemp()[1]
@@ -139,11 +139,9 @@ def test_remote_cancel_after_file_copy_file_deleted(self, is_cancelled_mock, can
         call_command("importcontent", "network", self.the_channel_id)
         # Check that the command itself was also cancelled.
         cancel_mock.assert_called_with()
-        # Check that the temp file we created where the first file was being downloaded to has been deleted
-        self.assertFalse(os.path.exists(local_path_1))
-        annotation_mock.mark_local_files_as_available.assert_not_called()
-        annotation_mock.set_leaf_node_availability_from_local_file_availability.assert_not_called()
-        annotation_mock.recurse_availability_up_tree.assert_not_called()
+        # Check that the temp file we created where the first file was being downloaded to has not been deleted
+        self.assertTrue(os.path.exists(local_path_1))
+        annotation_mock.set_availability.assert_called()
 
     @patch('kolibri.content.management.commands.importcontent.transfer.FileCopy')
     @patch('kolibri.content.management.commands.importcontent.AsyncCommand.cancel')
@@ -175,9 +173,7 @@ def test_local_cancel_during_transfer(self, is_cancelled_mock, cancel_mock, File
         FileCopyMock.assert_called_with(local_src_path, local_dest_path)
         FileCopyMock.assert_has_calls([call().cancel()])
         cancel_mock.assert_called_with()
-        annotation_mock.mark_local_files_as_available.assert_not_called()
-        annotation_mock.set_leaf_node_availability_from_local_file_availability.assert_not_called()
-        annotation_mock.recurse_availability_up_tree.assert_not_called()
+        annotation_mock.set_availability.assert_called()
 
 
 @override_settings(