Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨(converter) add edx to xapi video converter #211

Merged
merged 3 commits into from
Nov 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ and this project adheres to

## [Unreleased]

### Added

- EdX to xAPI converters for video events

### Changed

- Improve Ralph's library integration by unpinning dependencies (and prefer
Expand Down
4 changes: 2 additions & 2 deletions src/ralph/models/converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ def __init__(self, dest: str, src=None, transformers=lambda _: _, raw_input=Fals
Args:
dest (str): The destination path where to place the converted value.
src (str or None): The source from where the value to convert is fetched.
- When `src` is a path (ex. `context__user_id`) - the value is the item
When `src` is a path (ex. `context__user_id`), the value is the item
of the event at the path.
- When `src` is `None` - the value is the whole event.
When `src` is `None`, the value is the whole event.
transformers (function or tuple of functions): The function(s) to apply on
the source value.
raw_input (bool): Flag indicating whether `get_value` will receive a raw
Expand Down
7 changes: 7 additions & 0 deletions src/ralph/models/edx/converters/xapi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,10 @@

from .navigational import UIPageCloseToPageTerminated
from .server import ServerEventToPageViewed
from .video import (
UILoadVideoToVideoInitialized,
UIPauseVideoToVideoPaused,
UIPlayVideoToVideoPlayed,
UISeekVideoToVideoSeeked,
UIStopVideoToVideoTerminated,
)
215 changes: 215 additions & 0 deletions src/ralph/models/edx/converters/xapi/video.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
"""Video event xAPI Converter"""

from ralph.models.converter import ConversionItem
from ralph.models.edx.video.statements import (
UILoadVideo,
UIPauseVideo,
UIPlayVideo,
UISeekVideo,
UIStopVideo,
)
from ralph.models.xapi.constants import LANG_EN_US_DISPLAY
from ralph.models.xapi.video.constants import (
VIDEO_EXTENSION_LENGTH,
VIDEO_EXTENSION_PROGRESS,
VIDEO_EXTENSION_SESSION_ID,
VIDEO_EXTENSION_TIME,
VIDEO_EXTENSION_TIME_FROM,
VIDEO_EXTENSION_TIME_TO,
VIDEO_EXTENSION_USER_AGENT,
)
from ralph.models.xapi.video.statements import (
VideoInitialized,
VideoPaused,
VideoPlayed,
VideoSeeked,
VideoTerminated,
)

from .base import BaseXapiConverter


class VideoBaseXapiConverter(BaseXapiConverter):
"""Base Video xAPI Converter."""

def _get_conversion_items(self):
"""Returns a set of ConversionItems used for conversion."""

conversion_items = super()._get_conversion_items()
return conversion_items.union(
{
ConversionItem(
"object__definition__name",
"event__id",
lambda id: {LANG_EN_US_DISPLAY.__args__[0]: id},
),
ConversionItem(
"object__id",
None,
lambda event: self.platform_url
+ "/xblock/block-v1:"
+ event["context"]["course_id"]
+ "-course-v1:+type@video+block@"
+ event["event"]["id"],
),
quitterie-lcs marked this conversation as resolved.
Show resolved Hide resolved
ConversionItem(
"context__extensions__" + VIDEO_EXTENSION_SESSION_ID,
"session",
),
quitterie-lcs marked this conversation as resolved.
Show resolved Hide resolved
},
)


class UILoadVideoToVideoInitialized(VideoBaseXapiConverter):
quitterie-lcs marked this conversation as resolved.
Show resolved Hide resolved
"""Converts a common edX `load_video` event to xAPI."""

__src__ = UILoadVideo
__dest__ = VideoInitialized
quitterie-lcs marked this conversation as resolved.
Show resolved Hide resolved

def _get_conversion_items(self):
"""Returns a set of ConversionItems used for conversion."""

conversion_items = super()._get_conversion_items()
return conversion_items.union(
{
ConversionItem(
"context__extensions__" + VIDEO_EXTENSION_LENGTH,
None,
# Set the video length to null by default.
# This information is mandatory in the xAPI template
# and does not exist in the edX `load_video` event model.
lambda _: 0.0,
),
ConversionItem(
"context__extensions__" + VIDEO_EXTENSION_SESSION_ID,
"session",
),
ConversionItem(
"context__extensions__" + VIDEO_EXTENSION_USER_AGENT, "agent"
),
},
)


class UIPlayVideoToVideoPlayed(VideoBaseXapiConverter):
"""Converts a common edX `play_video` event to xAPI."""

__src__ = UIPlayVideo
__dest__ = VideoPlayed

def _get_conversion_items(self):
"""Returns a set of ConversionItems used for conversion."""

conversion_items = super()._get_conversion_items()
return conversion_items.union(
{
ConversionItem(
"result__extensions__" + VIDEO_EXTENSION_TIME,
"event__currentTime",
),
ConversionItem(
"context__extensions__" + VIDEO_EXTENSION_SESSION_ID,
"session",
),
},
)


class UIPauseVideoToVideoPaused(VideoBaseXapiConverter):
"""Converts a common edX `pause_video` event to xAPI."""

__src__ = UIPauseVideo
__dest__ = VideoPaused

def _get_conversion_items(self):
"""Returns a set of ConversionItems used for conversion."""

conversion_items = super()._get_conversion_items()
return conversion_items.union(
{
ConversionItem(
"result__extensions__" + VIDEO_EXTENSION_TIME,
"event__currentTime",
),
ConversionItem(
"context__extensions__" + VIDEO_EXTENSION_LENGTH,
None,
# Set the video length to null by default.
# This information is mandatory in the xAPI template
# and does not exist in the edX `pause_video` event model.
lambda _: 0.0,
),
ConversionItem(
"context__extensions__" + VIDEO_EXTENSION_SESSION_ID,
"session",
),
},
)


class UIStopVideoToVideoTerminated(VideoBaseXapiConverter):
"""Converts a common edX `stop_video` event to xAPI."""

__src__ = UIStopVideo
__dest__ = VideoTerminated

def _get_conversion_items(self):
"""Returns a set of ConversionItems used for conversion."""

conversion_items = super()._get_conversion_items()
return conversion_items.union(
{
ConversionItem(
"result__extensions__" + VIDEO_EXTENSION_TIME,
"event__currentTime",
),
ConversionItem(
"result__extensions__" + VIDEO_EXTENSION_PROGRESS,
None,
# Set the video progress to null by default.
# This information is mandatory in the xAPI template
# and does not exist in the edX `stop_video` event model.
lambda _: 0.0,
),
ConversionItem(
"context__extensions__" + VIDEO_EXTENSION_LENGTH,
None,
# Set the video length to null by default.
# This information is mandatory in the xAPI template
# and does not exist in the edX `stop_video` event model.
lambda _: 0.0,
),
ConversionItem(
"context__extensions__" + VIDEO_EXTENSION_SESSION_ID,
"session",
),
},
)


class UISeekVideoToVideoSeeked(VideoBaseXapiConverter):
"""Converts a common edX `seek_video` event to xAPI."""

__src__ = UISeekVideo
__dest__ = VideoSeeked

def _get_conversion_items(self):
"""Returns a set of ConversionItems used for conversion."""

conversion_items = super()._get_conversion_items()
return conversion_items.union(
{
ConversionItem(
"result__extensions__" + VIDEO_EXTENSION_TIME_FROM,
"event__old_time",
),
ConversionItem(
"result__extensions__" + VIDEO_EXTENSION_TIME_TO,
"event__new_time",
),
ConversionItem(
"context__extensions__" + VIDEO_EXTENSION_SESSION_ID,
"session",
),
},
)
4 changes: 3 additions & 1 deletion src/ralph/models/xapi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
from .navigation.statements import PageTerminated, PageViewed
from .video.statements import (
VideoCompleted,
VideoEnableClosedCaptioning,
VideoInitialized,
VideoInteracted,
VideoPaused,
VideoPlayed,
VideoScreenChangeInteraction,
VideoSeeked,
VideoTerminated,
VideoVolumeChangeInteraction,
)
8 changes: 4 additions & 4 deletions src/ralph/models/xapi/fields/verbs.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ class ViewedVerbField(VerbField):
"""Represents the `verb` xAPI Field for the action `viewed`.

Attributes:
id (str): Consists of the value `http://id.tincanapi.com/verb/viewed`.
display (dict): Consists of the dictionary `{"en-US": "viewed"}`.
id (str): Consists of the value `http://id.tincanapi.com/verb/viewed`.
display (dict): Consists of the dictionary `{"en-US": "viewed"}`.
"""

id: VERB_VIEWED_ID = VERB_VIEWED_ID.__args__[0]
Expand All @@ -43,8 +43,8 @@ class TerminatedVerbField(VerbField):
"""Represents the `verb` xAPI Field for the action `terminated`.

Attributes:
id (str): Consists of the value `http://adlnet.gov/expapi/verbs/terminated`.
display (dict): Consists of the dictionary `{"en-US": "terminated"}`.
id (str): Consists of the value `http://adlnet.gov/expapi/verbs/terminated`.
display (dict): Consists of the dictionary `{"en-US": "terminated"}`.
"""

id: VERB_TERMINATED_ID = VERB_TERMINATED_ID.__args__[0]
Expand Down
4 changes: 1 addition & 3 deletions src/ralph/models/xapi/video/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@
VIDEO_EXTENSION_CC_SUBTITLE_LANG = (
"https://w3id.org/xapi/video/extensions/cc-subtitle-lang"
)
VIDEO_EXTENSION_CC_SUBTITLE_ENABLED = (
"https://w3id.org/xapi/video/extensions/cc-subtitle-enabled"
)
VIDEO_EXTENSION_CC_ENABLED = "https://w3id.org/xapi/video/extensions/cc-enabled"
VIDEO_EXTENSION_COMPLETION_THRESHOLD = (
"https://w3id.org/xapi/video/extensions/completion-threshold"
)
Expand Down
Loading