shim.py 3.43 KB
Newer Older
1 2
"""Map new event context values to old top-level field values. Ensures events can be parsed by legacy parsers."""

3 4
import json

5
from .transformers import EventTransformerRegistry
6

7 8 9 10 11
CONTEXT_FIELDS_TO_INCLUDE = [
    'username',
    'session',
    'ip',
    'agent',
12 13 14
    'host',
    'referer',
    'accept_language'
15 16 17 18 19 20 21
]


class LegacyFieldMappingProcessor(object):
    """Ensures all required fields are included in emitted events"""

    def __call__(self, event):
22
        context = event.get('context', {})
23 24
        if 'context' in event:
            for field in CONTEXT_FIELDS_TO_INCLUDE:
25
                self.move_from_context(field, event)
26
            remove_shim_context(event)
27 28

        if 'data' in event:
29 30 31 32
            if context.get('event_source', '') == 'browser' and isinstance(event['data'], dict):
                event['event'] = json.dumps(event['data'])
            else:
                event['event'] = event['data']
33 34 35 36
            del event['data']
        else:
            event['event'] = {}

37 38 39 40
        if 'timestamp' in context:
            event['time'] = context['timestamp']
            del context['timestamp']
        elif 'timestamp' in event:
41
            event['time'] = event['timestamp']
42 43

        if 'timestamp' in event:
44 45
            del event['timestamp']

46 47 48 49 50 51 52 53 54 55 56 57
        self.move_from_context('event_type', event, event.get('name', ''))
        self.move_from_context('event_source', event, 'server')
        self.move_from_context('page', event, None)

    def move_from_context(self, field, event, default_value=''):
        """Move a field from the context to the top level of the event."""
        context = event.get('context', {})
        if field in context:
            event[field] = context[field]
            del context[field]
        else:
            event[field] = default_value
58 59 60


def remove_shim_context(event):
61 62 63
    """
    Remove obsolete fields from event context.
    """
64 65
    if 'context' in event:
        context = event['context']
66 67
        # These fields are present elsewhere in the event at this point
        context_fields_to_remove = set(CONTEXT_FIELDS_TO_INCLUDE)
68
        # This field is only used for Segment web analytics and does not concern researchers
69 70
        context_fields_to_remove.add('client_id')
        for field in context_fields_to_remove:
71 72
            if field in context:
                del context[field]
73 74


75 76 77 78 79 80 81 82 83
class GoogleAnalyticsProcessor(object):
    """Adds course_id as label, and sets nonInteraction property"""

    # documentation of fields here: https://segment.com/docs/integrations/google-analytics/
    # this should *only* be used on events destined for segment.com and eventually google analytics
    def __call__(self, event):
        context = event.get('context', {})
        course_id = context.get('course_id')

Gabe Mulley committed
84
        copied_event = event.copy()
85
        if course_id is not None:
Gabe Mulley committed
86
            copied_event['label'] = course_id
87

Gabe Mulley committed
88 89 90
        copied_event['nonInteraction'] = 1

        return copied_event
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109


class PrefixedEventProcessor(object):
    """
    Process any events whose name or prefix (ending with a '.') is registered
    as an EventTransformer.
    """

    def __call__(self, event):
        """
        If the event is registered with the EventTransformerRegistry, transform
        it.  Otherwise do nothing to it, and continue processing.
        """
        try:
            event = EventTransformerRegistry.create_transformer(event)
        except KeyError:
            return
        event.transform()
        return event