from kolibri.auth.api import KolibriAuthPermissions, KolibriAuthPermissionsFilter
from kolibri.content.models import ChannelMetadata, ContentNode
from kolibri.core.api import CSVModelViewSet
from rest_framework import serializers

from .models import ContentSessionLog, ContentSummaryLog


class LogCSVSerializerBase(serializers.ModelSerializer):

    username = serializers.SerializerMethodField()
    facility_name = serializers.SerializerMethodField()
    channel_name = serializers.SerializerMethodField()
    content_title = serializers.SerializerMethodField()
    time_spent = serializers.SerializerMethodField()
    progress = serializers.SerializerMethodField()

    def get_username(self, obj):
        if obj.user:
            return obj.user.username
        else:
            return ""

    def get_facility_name(self, obj):
        if obj.user:
            return obj.user.facility.name
        else:
            return ""

    def get_channel_name(self, obj):
        try:
            channel = ChannelMetadata.objects.get(id=obj.channel_id)
        except ChannelMetadata.DoesNotExist:
            return ""
        return channel.name

    def get_content_title(self, obj):
        channel = ChannelMetadata.objects.get(id=obj.channel_id)
        node = ContentNode.objects.filter(tree_id=channel.root.tree_id).first()
        if node:
            return node.title
        else:
            return ""

    def get_time_spent(self, obj):
        return str("{:.1f}".format(round(obj.time_spent, 1)))

    def get_progress(self, obj):
        return str("{:.4f}".format(round(obj.progress, 4)))


class ContentSummaryLogCSVSerializer(LogCSVSerializerBase):

    class Meta:
        model = ContentSummaryLog
        fields = ('username', 'facility_name', 'content_id', 'content_title', 'channel_id', 'channel_name', 'start_timestamp',
                  'end_timestamp', 'completion_timestamp', 'time_spent', 'progress', 'kind')
        labels = {
            "start_timestamp": "Time of first interaction",
            "end_timestamp": "Time of last interaction",
            "completion_timestamp": "Time of completion",
            "time_spent": "Time Spent (sec)",
            "progress": "Progress (0-1)",
        }


class ContentSessionLogCSVSerializer(LogCSVSerializerBase):

    class Meta:
        model = ContentSessionLog
        fields = ('username', 'facility_name', 'content_id', 'content_title', 'channel_id', 'channel_name', 'start_timestamp',
                  'end_timestamp', 'time_spent', 'progress', 'kind')
        labels = {
            "start_timestamp": "Time of first interaction",
            "end_timestamp": "Time of last interaction",
            "time_spent": "Time Spent (sec)",
            "progress": "Progress (0-1)",
        }


class ContentSummaryLogCSVExportViewSet(CSVModelViewSet):
    permission_classes = (KolibriAuthPermissions,)
    filter_backends = (KolibriAuthPermissionsFilter,)
    queryset = ContentSummaryLog.objects.all()
    serializer_class = ContentSummaryLogCSVSerializer
    csv_export_filename = 'content_summary_logs'


class ContentSessionLogCSVExportViewSet(CSVModelViewSet):
    permission_classes = (KolibriAuthPermissions,)
    filter_backends = (KolibriAuthPermissionsFilter,)
    queryset = ContentSessionLog.objects.all()
    serializer_class = ContentSessionLogCSVSerializer
    csv_export_filename = 'content_session_logs'

kolibri/logger/csv.py
from kolibri.auth.api import KolibriAuthPermissions, KolibriAuthPermissionsFilter
from kolibri.content.models import ChannelMetadata, ContentNode
from kolibri.core.api import CSVModelViewSet
from rest_framework import serializers

from .models import ContentSessionLog, ContentSummaryLog


class LogCSVSerializerBase(serializers.ModelSerializer):

    username = serializers.SerializerMethodField()
    facility_name = serializers.SerializerMethodField()
    channel_name = serializers.SerializerMethodField()
    content_title = serializers.SerializerMethodField()
    time_spent = serializers.SerializerMethodField()
    progress = serializers.SerializerMethodField()

    def get_username(self, obj):
        if obj.user:
            return obj.user.username
        else:
            return ""

    def get_facility_name(self, obj):
        if obj.user:
            return obj.user.facility.name
        else:
            return ""

    def get_channel_name(self, obj):
        try:
            channel = ChannelMetadata.objects.get(id=obj.channel_id)
        except ChannelMetadata.DoesNotExist:
            return ""
        return channel.name

    def get_content_title(self, obj):
        node = ContentNode.objects.filter(content_id=obj.content_id).first()
        if node:
            return node.title
        else:
            return ""

    def get_time_spent(self, obj):
        return str("{:.1f}".format(round(obj.time_spent, 1)))

    def get_progress(self, obj):
        return str("{:.4f}".format(round(obj.progress, 4)))


class ContentSummaryLogCSVSerializer(LogCSVSerializerBase):

    class Meta:
        model = ContentSummaryLog
        fields = ('username', 'facility_name', 'content_id', 'content_title', 'channel_id', 'channel_name', 'start_timestamp',
                  'end_timestamp', 'completion_timestamp', 'time_spent', 'progress', 'kind')
        labels = {
            "start_timestamp": "Time of first interaction",
            "end_timestamp": "Time of last interaction",
            "completion_timestamp": "Time of completion",
            "time_spent": "Time Spent (sec)",
            "progress": "Progress (0-1)",
        }


class ContentSessionLogCSVSerializer(LogCSVSerializerBase):

    class Meta:
        model = ContentSessionLog
        fields = ('username', 'facility_name', 'content_id', 'content_title', 'channel_id', 'channel_name', 'start_timestamp',
                  'end_timestamp', 'time_spent', 'progress', 'kind')
        labels = {
            "start_timestamp": "Time of first interaction",
            "end_timestamp": "Time of last interaction",
            "time_spent": "Time Spent (sec)",
            "progress": "Progress (0-1)",
        }


class ContentSummaryLogCSVExportViewSet(CSVModelViewSet):
    permission_classes = (KolibriAuthPermissions,)
    filter_backends = (KolibriAuthPermissionsFilter,)
    queryset = ContentSummaryLog.objects.all()
    serializer_class = ContentSummaryLogCSVSerializer
    csv_export_filename = 'content_summary_logs'


class ContentSessionLogCSVExportViewSet(CSVModelViewSet):
    permission_classes = (KolibriAuthPermissions,)
    filter_backends = (KolibriAuthPermissionsFilter,)
    queryset = ContentSessionLog.objects.all()
    serializer_class = ContentSessionLogCSVSerializer
    csv_export_filename = 'content_session_logs'

kolibri/logger/csv.py
diff --git a/kolibri/logger/csv.py b/kolibri/logger/csv.py
index 617365c620c..d1c50e0524f 100644
--- a/kolibri/logger/csv.py
+++ b/kolibri/logger/csv.py
@@ -35,8 +35,7 @@ def get_channel_name(self, obj):
         return channel.name
 
     def get_content_title(self, obj):
-        channel = ChannelMetadata.objects.get(id=obj.channel_id)
-        node = ContentNode.objects.filter(tree_id=channel.root.tree_id).first()
+        node = ContentNode.objects.filter(content_id=obj.content_id).first()
         if node:
             return node.title
         else:
diff --git a/kolibri/logger/test/test_api.py b/kolibri/logger/test/test_api.py
index 962d0edeca5..4c7e13d5eca 100644
--- a/kolibri/logger/test/test_api.py
+++ b/kolibri/logger/test/test_api.py
@@ -12,6 +12,7 @@
 from rest_framework import status
 from rest_framework.test import APITestCase
 
+from kolibri.content.models import ChannelMetadata, ContentNode
 from kolibri.core.exams.models import Exam
 from kolibri.logger.models import ExamLog
 
@@ -326,6 +327,17 @@ def test_csv_download(self):
             self.assertEqual(len(results[0]), len(row))
         self.assertEqual(len(results[1:]), expected_count)
 
+    def test_csv_download_deleted_content(self):
+        self.client.login(username=self.admin.username, password=DUMMY_PASSWORD, facility=self.facility)
+        expected_count = ContentSummaryLog.objects.count()
+        ContentNode.objects.all().delete()
+        ChannelMetadata.objects.all().delete()
+        response = self.client.get(reverse('contentsummarylogcsv-list'))
+        results = list(csv.reader(row for row in response.content.decode("utf-8").split("\n") if row))
+        for row in results[1:]:
+            self.assertEqual(len(results[0]), len(row))
+        self.assertEqual(len(results[1:]), expected_count)
+
 
 class ContentSessionLogCSVExportTestCase(APITestCase):
 
@@ -353,6 +365,17 @@ def test_csv_download(self):
             self.assertEqual(len(results[0]), len(row))
         self.assertEqual(len(results[1:]), expected_count)
 
+    def test_csv_download_deleted_content(self):
+        self.client.login(username=self.admin.username, password=DUMMY_PASSWORD, facility=self.facility)
+        expected_count = ContentSessionLog.objects.count()
+        ContentNode.objects.all().delete()
+        ChannelMetadata.objects.all().delete()
+        response = self.client.get(reverse('contentsessionlogcsv-list'))
+        results = list(csv.reader(row for row in response.content.decode("utf-8").split("\n") if row))
+        for row in results[1:]:
+            self.assertEqual(len(results[0]), len(row))
+        self.assertEqual(len(results[1:]), expected_count)
+
 
 class ExamAttemptLogAPITestCase(APITestCase):
 

CSV export fails if logs refer to channel that no longer exists locally
### Observed behavior

Tried to export CSV log via http://kolibribeta.learningequality.org/management/facility/#/data and got a 500 (traceback below). I think the database summary logs referred to a channel that we since deleted. We try to extract the human-readable channel names to include as a column in the CSV file, but this blows up if we don't have the channel metadata anymore.

### Expected behavior

Should have exported the CSV successfully, and just skipped the title for any channels it couldn't find, and leave those cells blank.

### User-facing consequences

Can completely block the CSV export feature from working.

### Errors and logs

```
ERROR 2017-11-30 23:09:11,954 base Internal Server Error: /api/contentsessionlogcsv/
Traceback (most recent call last):
  File "/var/www/.pex/install/kolibri-0.7.0.dev020171130214228_git-py2.py3-none-any.whl.3f4dacee3f158ff5e739b8a2a0b0b1cdb1b7a8e1/kolibri-0.7.0.dev020171130214228_git-py2.py3-none-any.whl/kolibri/dist/django/core/handlers/base.py", line 149, in get_response
    response = self.process_exception_by_middleware(e, request)
  File "/var/www/.pex/install/kolibri-0.7.0.dev020171130214228_git-py2.py3-none-any.whl.3f4dacee3f158ff5e739b8a2a0b0b1cdb1b7a8e1/kolibri-0.7.0.dev020171130214228_git-py2.py3-none-any.whl/kolibri/dist/django/core/handlers/base.py", line 147, in get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/var/www/.pex/install/kolibri-0.7.0.dev020171130214228_git-py2.py3-none-any.whl.3f4dacee3f158ff5e739b8a2a0b0b1cdb1b7a8e1/kolibri-0.7.0.dev020171130214228_git-py2.py3-none-any.whl/kolibri/dist/django/views/decorators/csrf.py", line 58, in wrapped_view
    return view_func(*args, **kwargs)
  File "/var/www/.pex/install/kolibri-0.7.0.dev020171130214228_git-py2.py3-none-any.whl.3f4dacee3f158ff5e739b8a2a0b0b1cdb1b7a8e1/kolibri-0.7.0.dev020171130214228_git-py2.py3-none-any.whl/kolibri/dist/rest_framework/viewsets.py", line 87, in view
    return self.dispatch(request, *args, **kwargs)
  File "/var/www/.pex/install/kolibri-0.7.0.dev020171130214228_git-py2.py3-none-any.whl.3f4dacee3f158ff5e739b8a2a0b0b1cdb1b7a8e1/kolibri-0.7.0.dev020171130214228_git-py2.py3-none-any.whl/kolibri/dist/rest_framework/views.py", line 466, in dispatch
    response = self.handle_exception(exc)
  File "/var/www/.pex/install/kolibri-0.7.0.dev020171130214228_git-py2.py3-none-any.whl.3f4dacee3f158ff5e739b8a2a0b0b1cdb1b7a8e1/kolibri-0.7.0.dev020171130214228_git-py2.py3-none-any.whl/kolibri/dist/rest_framework/views.py", line 463, in dispatch
    response = handler(request, *args, **kwargs)
  File "/var/www/.pex/install/kolibri-0.7.0.dev020171130214228_git-py2.py3-none-any.whl.3f4dacee3f158ff5e739b8a2a0b0b1cdb1b7a8e1/kolibri-0.7.0.dev020171130214228_git-py2.py3-none-any.whl/kolibri/dist/rest_framework/mixins.py", line 48, in list
    return Response(serializer.data)
  File "/var/www/.pex/install/kolibri-0.7.0.dev020171130214228_git-py2.py3-none-any.whl.3f4dacee3f158ff5e739b8a2a0b0b1cdb1b7a8e1/kolibri-0.7.0.dev020171130214228_git-py2.py3-none-any.whl/kolibri/dist/rest_framework/serializers.py", line 674, in data
    ret = super(ListSerializer, self).data
  File "/var/www/.pex/install/kolibri-0.7.0.dev020171130214228_git-py2.py3-none-any.whl.3f4dacee3f158ff5e739b8a2a0b0b1cdb1b7a8e1/kolibri-0.7.0.dev020171130214228_git-py2.py3-none-any.whl/kolibri/dist/rest_framework/serializers.py", line 239, in data
    self._data = self.to_representation(self.instance)
  File "/var/www/.pex/install/kolibri-0.7.0.dev020171130214228_git-py2.py3-none-any.whl.3f4dacee3f158ff5e739b8a2a0b0b1cdb1b7a8e1/kolibri-0.7.0.dev020171130214228_git-py2.py3-none-any.whl/kolibri/dist/rest_framework/serializers.py", line 614, in to_representation
    self.child.to_representation(item) for item in iterable
  File "/var/www/.pex/install/kolibri-0.7.0.dev020171130214228_git-py2.py3-none-any.whl.3f4dacee3f158ff5e739b8a2a0b0b1cdb1b7a8e1/kolibri-0.7.0.dev020171130214228_git-py2.py3-none-any.whl/kolibri/dist/rest_framework/serializers.py", line 472, in to_representation
    ret[field.field_name] = field.to_representation(attribute)
  File "/var/www/.pex/install/kolibri-0.7.0.dev020171130214228_git-py2.py3-none-any.whl.3f4dacee3f158ff5e739b8a2a0b0b1cdb1b7a8e1/kolibri-0.7.0.dev020171130214228_git-py2.py3-none-any.whl/kolibri/dist/rest_framework/fields.py", line 1653, in to_representation
    return method(value)
  File "/var/www/.pex/install/kolibri-0.7.0.dev020171130214228_git-py2.py3-none-any.whl.3f4dacee3f158ff5e739b8a2a0b0b1cdb1b7a8e1/kolibri-0.7.0.dev020171130214228_git-py2.py3-none-any.whl/kolibri/logger/csv.py", line 38, in get_content_title
    channel = ChannelMetadata.objects.get(id=obj.channel_id)
  File "/var/www/.pex/install/kolibri-0.7.0.dev020171130214228_git-py2.py3-none-any.whl.3f4dacee3f158ff5e739b8a2a0b0b1cdb1b7a8e1/kolibri-0.7.0.dev020171130214228_git-py2.py3-none-any.whl/kolibri/dist/django/db/models/manager.py", line 122, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/var/www/.pex/install/kolibri-0.7.0.dev020171130214228_git-py2.py3-none-any.whl.3f4dacee3f158ff5e739b8a2a0b0b1cdb1b7a8e1/kolibri-0.7.0.dev020171130214228_git-py2.py3-none-any.whl/kolibri/dist/django/db/models/query.py", line 387, in get
    self.model._meta.object_name
DoesNotExist: ChannelMetadata matching query does not exist.
```

### Steps to reproduce

1. Download a channel
2. Interact with content to generate logs
3. Delete the channel
4. Try to export the CSV file

### Context

* Kolibri version: 0.7.0.dev020171130214228-git
* Operating system: Ubuntu
* Browser: Chrome

