Repository: learningequality/kolibri

Issue:
importcontent gives a TypeError if channel metadata is not present
<!--
Instructions:
 * Fill out the sections below, replace …'s with information about your issue
 * Use the 'preview' function above this text box to verify formatting before submitting
-->

### Observed behavior
<!--
Description of the behavior that was observed, including screenshots or other references when applicable
-->

Executing `kolibri manage importcontent` without having somehow fetched the channel metadata results in the following error -

```
...
  File "/usr/lib/python3/dist-packages/kolibri/core/content/utils/annotation.py", line 237, in _calculate_batch_params
    min(CHUNKSIZE, ceil(250 * max_rght / (constraint_count or 1)))
TypeError: unsupported operand type(s) for *: 'int' and 'NoneType'
```

### Expected behavior
<!--
Description of what behavior was expected but did not occur
-->

`importcontent` should either terminate with an appropriate message or implicitly execute `importchannel`. 

### User-facing consequences
<!--
Implications and real-world consequences for learners, coaches, admins, and other users of the application
-->

Confusion.

### Errors and logs
<!--
Relevant logs from:
 * the command line
 * ~/.kolibri/logs/kolibri.txt
 * the browser console

Please wrap errors in triple backticks for clean formatting like this:
```
01:10 info: something happened
01:12 error: something bad happened
```
-->

```
$ kolibri manage importcontent --node_ids 1dd8f2bf51325a1dacf95e0dadd9f9ea,0d2ebd88cc9653dda5703413a8c5396c network 2fd54ca47a8f59c99fcefaaa3894c19e
WARNING:root:No C Extensions available for this platform.

/usr/lib/python3/dist-packages/kolibri/dist/rest_framework/utils/serializer_helpers.py:107: SyntaxWarning: "is" with a literal. Did you mean "=="?
  if value is None or value is '':
INFO     Running Kolibri with the following settings: kolibri.deployment.default.settings.base
WARNING  Redis is configured without a maximum memory policy. Using Redis with Kolibri, the following is suggested: maxmemory-policy allkeys-lru
WARNING  Redis is configured without a maximum memory size.
WARNING  Problematic Redis settings detected, please see Redis configuration documentation for details: https://redis.io/topics/config
INFO     Invoking command importcontent --node_ids 1dd8f2bf51325a1dacf95e0dadd9f9ea,0d2ebd88cc9653dda5703413a8c5396c network 2fd54ca47a8f59c99fcefaaa3894c19e
Traceback (most recent call last):
  File "/usr/bin/kolibri", line 11, in <module>
    load_entry_point('kolibri==0.14.3', 'console_scripts', 'kolibri')()
  File "/usr/lib/python3/dist-packages/kolibri/dist/click/core.py", line 764, in __call__
    return self.main(*args, **kwargs)
  File "/usr/lib/python3/dist-packages/kolibri/dist/click/core.py", line 717, in main
    rv = self.invoke(ctx)
  File "/usr/lib/python3/dist-packages/kolibri/dist/click/core.py", line 1137, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/usr/lib/python3/dist-packages/kolibri/utils/cli.py", line 277, in invoke
    return super(KolibriDjangoCommand, self).invoke(ctx)
  File "/usr/lib/python3/dist-packages/kolibri/dist/click/core.py", line 956, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/usr/lib/python3/dist-packages/kolibri/dist/click/core.py", line 555, in invoke
    return callback(*args, **kwargs)
  File "/usr/lib/python3/dist-packages/kolibri/dist/click/decorators.py", line 17, in new_func
    return f(get_current_context(), *args, **kwargs)
  File "/usr/lib/python3/dist-packages/kolibri/utils/cli.py", line 727, in manage
    execute_from_command_line(["kolibri manage"] + ctx.args)
  File "/usr/lib/python3/dist-packages/kolibri/dist/django/core/management/__init__.py", line 364, in execute_from_command_line
    utility.execute()
  File "/usr/lib/python3/dist-packages/kolibri/dist/django/core/management/__init__.py", line 356, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/usr/lib/python3/dist-packages/kolibri/dist/django/core/management/base.py", line 283, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/usr/lib/python3/dist-packages/kolibri/dist/django/core/management/base.py", line 330, in execute
    output = self.handle(*args, **options)
  File "/usr/lib/python3/dist-packages/kolibri/core/tasks/management/commands/base.py", line 113, in handle
    return self.handle_async(*args, **options)
  File "/usr/lib/python3/dist-packages/kolibri/core/content/management/commands/importcontent.py", line 441, in handle_async
    self.download_content(
  File "/usr/lib/python3/dist-packages/kolibri/core/content/management/commands/importcontent.py", line 159, in download_content
    self._transfer(
  File "/usr/lib/python3/dist-packages/kolibri/core/content/management/commands/importcontent.py", line 366, in _transfer
    annotation.set_content_visibility(
  File "/usr/lib/python3/dist-packages/kolibri/core/content/utils/annotation.py", line 681, in set_content_visibility
    update_content_metadata(
  File "/usr/lib/python3/dist-packages/kolibri/core/content/utils/annotation.py", line 669, in update_content_metadata
    set_leaf_node_availability_from_local_file_availability(
  File "/usr/lib/python3/dist-packages/kolibri/core/content/utils/annotation.py", line 350, in set_leaf_node_availability_from_local_file_availability
    max_rght, dynamic_chunksize = _calculate_batch_params(
  File "/usr/lib/python3/dist-packages/kolibri/core/content/utils/annotation.py", line 237, in _calculate_batch_params
    min(CHUNKSIZE, ceil(250 * max_rght / (constraint_count or 1)))
TypeError: unsupported operand type(s) for *: 'int' and 'NoneType'
```

### Steps to reproduce
<!--
Precise steps that someone else can follow in order to see this behavior
-->

Execute `importcontent` without executing `importchannel` or having browsed the channel via a `DEVICE - CHANNELS - IMPORT` attempt. 

### Context
<!--
Tell us about your environment, including:
 * Kolibri version
 * Operating system
 * Browser
-->

Kolibri 0.14.3


Discussion:

Resolving patch:
diff --git a/kolibri/core/content/management/commands/importcontent.py b/kolibri/core/content/management/commands/importcontent.py
--- a/kolibri/core/content/management/commands/importcontent.py
+++ b/kolibri/core/content/management/commands/importcontent.py
@@ -10,6 +10,7 @@
 from ...utils import paths
 from ...utils import transfer
 from kolibri.core.content.errors import InvalidStorageFilenameError
+from kolibri.core.content.models import ChannelMetadata
 from kolibri.core.content.models import ContentNode
 from kolibri.core.content.utils.file_availability import LocationError
 from kolibri.core.content.utils.import_export_content import compare_checksums
@@ -453,6 +454,16 @@ def _start_file_transfer(self, f, filetransfer):
         return FILE_TRANSFERRED, data_transferred
 
     def handle_async(self, *args, **options):
+        try:
+            ChannelMetadata.objects.get(id=options["channel_id"])
+        except ValueError:
+            raise CommandError(
+                "{} is not a valid channel_id".format(options["channel_id"])
+            )
+        except ChannelMetadata.DoesNotExist:
+            raise CommandError(
+                "Must import a channel with importchannel before importing content."
+            )
         if options["command"] == "network":
             self.download_content(
                 options["channel_id"],


Test patch: