event_transformers.py 5.29 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
"""
Transformers for Discussion-related events.
"""
from django.contrib.auth.models import User
from django.core.urlresolvers import reverse, NoReverseMatch

from eventtracking.processors.exceptions import EventEmissionExit
from opaque_keys import InvalidKeyError
from opaque_keys.edx.locator import CourseLocator

from django_comment_client.base.views import add_truncated_title_to_event_data
from django_comment_client.permissions import get_team
from django_comment_client.utils import get_cached_discussion_id_map_by_course_id
from track.transformers import EventTransformer, EventTransformerRegistry
from track.views.segmentio import (
    BI_SCREEN_VIEWED_EVENT_NAME,
    FORUM_THREAD_VIEWED_EVENT_LABEL
)


def _get_string(dictionary, key, del_if_bad=True):
    """
    Get a string from a dictionary by key.

    If the key is not in the dictionary or does not refer to a string:
        - Return None
        - Optionally delete the key (del_if_bad)
    """
    if key in dictionary:
        value = dictionary[key]
        if isinstance(value, basestring):
            return value
        else:
            if del_if_bad:
                del dictionary[key]
            return None
    else:
        return None


@EventTransformerRegistry.register
class ForumThreadViewedEventTransformer(EventTransformer):
    """
    Transformer for forum-thread-viewed mobile navigation events.
    """

    match_key = BI_SCREEN_VIEWED_EVENT_NAME

    def process_event(self):
        """
        Process incoming mobile navigation events.

        For forum-thread-viewed events, change their names to
        edx.forum.thread.viewed and manipulate their data to conform with
        edx.forum.thread.viewed event design.

        Throw out other events.
        """

        # Get event context dict
        # Throw out event if context nonexistent or wrong type
        context = self.get('context')
        if not isinstance(context, dict):
            raise EventEmissionExit()

        # Throw out event if it's not a forum thread view
        if _get_string(context, 'label', del_if_bad=False) != FORUM_THREAD_VIEWED_EVENT_LABEL:
            raise EventEmissionExit()

        # Change name and event type
        self['name'] = 'edx.forum.thread.viewed'
        self['event_type'] = self['name']

        # If no event data, set it to an empty dict
        if 'event' not in self:
            self['event'] = {}
            self.event = {}

        # Throw out the context dict within the event data
        # (different from the context dict extracted above)
        if 'context' in self.event:
            del self.event['context']

        # Parse out course key
        course_id_string = _get_string(context, 'course_id') if context else None
        course_id = None
        if course_id_string:
            try:
                course_id = CourseLocator.from_string(course_id_string)
            except InvalidKeyError:
                pass

        # Change 'thread_id' field to 'id'
        thread_id = _get_string(self.event, 'thread_id')
        if thread_id:
            del self.event['thread_id']
            self.event['id'] = thread_id

        # Change 'topic_id' to 'commentable_id'
        commentable_id = _get_string(self.event, 'topic_id')
        if commentable_id:
            del self.event['topic_id']
            self.event['commentable_id'] = commentable_id

        # Change 'action' to 'title' and truncate
        title = _get_string(self.event, 'action')
        if title is not None:
            del self.event['action']
            add_truncated_title_to_event_data(self.event, title)

        # Change 'author' to 'target_username'
        author = _get_string(self.event, 'author')
        if author is not None:
            del self.event['author']
            self.event['target_username'] = author

        # Load user
        username = _get_string(self, 'username')
        user = None
        if username:
            try:
                user = User.objects.get(username=username)
            except User.DoesNotExist:
                pass

        # If in a category, add category name and ID
        if course_id and commentable_id and user:
            id_map = get_cached_discussion_id_map_by_course_id(course_id, [commentable_id], user)
            if commentable_id in id_map:
                self.event['category_name'] = id_map[commentable_id]['title']
                self.event['category_id'] = commentable_id

        # Add thread URL
        if course_id and commentable_id and thread_id:
            url_kwargs = {
                'course_id': course_id_string,
                'discussion_id': commentable_id,
                'thread_id': thread_id
            }
            try:
                self.event['url'] = reverse('single_thread', kwargs=url_kwargs)
            except NoReverseMatch:
                pass

        # Add user's forum and course roles
        if course_id and user:
            self.event['user_forums_roles'] = [
                role.name for role in user.roles.filter(course_id=course_id)
            ]
            self.event['user_course_roles'] = [
                role.role for role in user.courseaccessrole_set.filter(course_id=course_id)
            ]

        # Add team ID
        if commentable_id:
            team = get_team(commentable_id)
            if team:
                self.event['team_id'] = team.team_id