diff --git a/event_routing_backends/processors/caliper/event_transformers/enrollment_events.py b/event_routing_backends/processors/caliper/event_transformers/enrollment_events.py index dfdda372..74c27a55 100644 --- a/event_routing_backends/processors/caliper/event_transformers/enrollment_events.py +++ b/event_routing_backends/processors/caliper/event_transformers/enrollment_events.py @@ -50,10 +50,11 @@ def get_object(self): # TODO: replace with anonymous enrollment id? course_root_url = self.get_object_iri('course', self.get_data('data.course_id', True)) - caliper_object = { + caliper_object = super().get_object() + caliper_object.update({ 'id': course_root_url, 'type': 'CourseOffering', 'name': course['display_name'], 'extensions': {'mode': self.get_data('data.mode')} if self.get_data('data.mode') is not None else None, - } + }) return caliper_object diff --git a/event_routing_backends/processors/caliper/event_transformers/navigation_events.py b/event_routing_backends/processors/caliper/event_transformers/navigation_events.py index 8c322ecc..50171dc8 100644 --- a/event_routing_backends/processors/caliper/event_transformers/navigation_events.py +++ b/event_routing_backends/processors/caliper/event_transformers/navigation_events.py @@ -39,7 +39,7 @@ def get_object(self): dict """ self.backend_name = 'caliper' - caliper_object = self.transformed_event['object'] + caliper_object = super().get_object() data = self.get_data('data') extensions = {} diff --git a/event_routing_backends/processors/caliper/event_transformers/problem_interaction_events.py b/event_routing_backends/processors/caliper/event_transformers/problem_interaction_events.py index 38f0b36d..23e007f5 100644 --- a/event_routing_backends/processors/caliper/event_transformers/problem_interaction_events.py +++ b/event_routing_backends/processors/caliper/event_transformers/problem_interaction_events.py @@ -149,7 +149,7 @@ def get_object(self): else: iri_url = object_id - caliper_object = self.transformed_event['object'] + caliper_object = super().get_object() caliper_object.update({ 'id': self.get_object_iri('xblock', iri_url), 'type': OBJECT_TYPE_MAP.get(key, 'Attempt'), diff --git a/event_routing_backends/processors/caliper/event_transformers/video_events.py b/event_routing_backends/processors/caliper/event_transformers/video_events.py index d202a348..87bc3516 100644 --- a/event_routing_backends/processors/caliper/event_transformers/video_events.py +++ b/event_routing_backends/processors/caliper/event_transformers/video_events.py @@ -82,7 +82,7 @@ def get_object(self): dict """ self.backend_name = 'caliper' - caliper_object = self.transformed_event['object'] + caliper_object = super().get_object() data = self.get_data('data') course_id = self.get_data('context.course_id', True) video_id = self.get_data('data.id', True) @@ -176,10 +176,12 @@ class VideoSpeedChangedTransformer(BaseVideoTransformer): """ Transform the event fired when a video's speed is changed. """ - additional_fields = ('target', 'extensions',) + additional_fields = ('target',) def get_extensions(self): - return { + extensions = super().get_extensions() + extensions.update({ 'oldSpeed': self.get_data('old_speed'), 'newSpeed': self.get_data('new_speed'), - } + }) + return extensions diff --git a/event_routing_backends/processors/caliper/transformer.py b/event_routing_backends/processors/caliper/transformer.py index 0ec532eb..0f7e9496 100644 --- a/event_routing_backends/processors/caliper/transformer.py +++ b/event_routing_backends/processors/caliper/transformer.py @@ -19,41 +19,35 @@ class CaliperTransformer(BaseTransformerMixin): required_fields = ( 'type', 'object', - 'action' + 'action', + 'extensions', ) - def base_transform(self): + def base_transform(self, transformed_event): """ Transform common Caliper fields. """ - self._add_generic_fields() - self._add_actor_info() - self._add_session_info() + transformed_event = super().base_transform(transformed_event) + self._add_generic_fields(transformed_event) + self._add_actor_info(transformed_event) + self._add_session_info(transformed_event) + return transformed_event - def _add_generic_fields(self): + def _add_generic_fields(self, transformed_event): """ Add all of the generic fields to the transformed_event object. """ - self.transformed_event.update({ + transformed_event.update({ '@context': CALIPER_EVENT_CONTEXT, 'id': uuid.uuid4().urn, 'eventTime': convert_datetime_to_iso(self.get_data('timestamp', True)), - 'extensions': {} }) - self.transformed_event['object'] = {} - course_id = self.get_data('context.course_id') - if course_id is not None: - extensions = {"isPartOf": {}} - extensions['isPartOf']['id'] = self.get_object_iri('course', course_id) - extensions['isPartOf']['type'] = 'CourseOffering' - self.transformed_event['object']['extensions'] = {} - self.transformed_event['object']['extensions'].update(extensions) - def _add_actor_info(self): + def _add_actor_info(self, transformed_event): """ - Add all generic information related to `actor`. + Add all generic information related to `actor` to the transformed_event. """ - self.transformed_event['actor'] = { + transformed_event['actor'] = { 'id': self.get_object_iri( 'user', get_anonymous_user_id(self.extract_username_or_userid(), 'CALIPER'), @@ -61,16 +55,45 @@ def _add_actor_info(self): 'type': 'Person' } - def _add_session_info(self): + def _add_session_info(self, transformed_event): """ - Add session info related to the event + Add session info related to the transformed_event. """ sessionid = self.extract_sessionid() if sessionid: - self.transformed_event['session'] = { + transformed_event['session'] = { 'id': self.get_object_iri( 'sessions', sessionid, ), 'type': 'Session' } + + def get_object(self): + """ + Return object for the event. + + Returns: + dict + """ + caliper_object = super().get_object() + course_id = self.get_data('context.course_id') + if course_id is not None: + extensions = {"isPartOf": {}} + extensions['isPartOf']['id'] = self.get_object_iri('course', course_id) + extensions['isPartOf']['type'] = 'CourseOffering' + caliper_object['extensions'] = {} + caliper_object['extensions'].update(extensions) + + return caliper_object + + def get_extensions(self): + """ + Return extensions for the event. + + Returns: + dict + """ + return { + 'transformerVersion': self.transformer_version, + } diff --git a/event_routing_backends/processors/mixins/base_transformer.py b/event_routing_backends/processors/mixins/base_transformer.py index 78cf8398..a4c78743 100644 --- a/event_routing_backends/processors/mixins/base_transformer.py +++ b/event_routing_backends/processors/mixins/base_transformer.py @@ -30,7 +30,6 @@ def __init__(self, event): event (dict) : event to be transformed """ self.event = event.copy() - self.transformed_event = {} @staticmethod def find_nested(source_dict, key): @@ -65,14 +64,17 @@ def _find_nested(event_dict): return _find_nested(source_dict) - def base_transform(self): + def base_transform(self, transformed_event): """ Transform the fields that are common for all events. Other classes can override this method to add common transformation code for events. + + Returns: + ANY """ - return + return transformed_event or {} @property def transformer_version(self): @@ -92,18 +94,18 @@ def transform(self): Transform the edX event. Returns: - dict + ANY """ - self.base_transform() + transformed_event = self.base_transform({}) transforming_fields = self.required_fields + self.additional_fields for key in transforming_fields: if hasattr(self, key): value = getattr(self, key) - self.transformed_event[key] = value + transformed_event[key] = value elif hasattr(self, f'get_{key}'): value = getattr(self, f'get_{key}')() - self.transformed_event[key] = value + transformed_event[key] = value else: raise ValueError( 'Cannot find value for "{}" in transformer {} for the edx event "{}"'.format( @@ -111,12 +113,9 @@ def transform(self): ) ) - if self.backend_name == 'caliper': - self.transformed_event['extensions']['transformerVersion'] = self.transformer_version - - self.transformed_event = self.del_none(self.transformed_event) + transformed_event = self.del_none(transformed_event) - return self.transformed_event + return transformed_event def extract_username_or_userid(self): """ @@ -218,3 +217,12 @@ def get_object_iri(self, object_type, object_id): object_type=object_type, object_id=object_id ) + + def get_object(self): + """ + Return object for the event. + + Returns: + dict + """ + return {} diff --git a/event_routing_backends/processors/xapi/transformer.py b/event_routing_backends/processors/xapi/transformer.py index 74f2ff0e..ee2860ad 100644 --- a/event_routing_backends/processors/xapi/transformer.py +++ b/event_routing_backends/processors/xapi/transformer.py @@ -46,22 +46,28 @@ def transform(self): transformed_props["version"] = constants.XAPI_SPECIFICATION_VERSION return Statement(**transformed_props) - def base_transform(self): + def base_transform(self, transformed_event): """ Transform the fields that are common for all events. """ + transformed_event = super().base_transform(transformed_event) actor = self.get_actor() event_timestamp = self.get_timestamp() - self.transformed_event = { + transformed_event.update({ 'actor': actor, 'context': self.get_context(), 'timestamp': event_timestamp, - } + }) + transformed_event['actor'] = self.get_actor() + transformed_event['context'] = self.get_context() + transformed_event['timestamp'] = self.get_timestamp() + # Warning! changing anything in these 2 lines or changing the "base_uuid" can invalidate # billions of rows in the database. Please have a community discussion first before introducing # any change in generation of UUID. uuid_str = f'{actor.to_json()}-{event_timestamp}' - self.transformed_event['id'] = get_uuid5(self.verb.to_json(), uuid_str) # pylint: disable=no-member + transformed_event['id'] = get_uuid5(self.verb.to_json(), uuid_str) # pylint: disable=no-member + return transformed_event def get_actor(self): """