Commit 05d959bc by Gabe Mulley

Merge pull request #3089 from mulby/gabe/publish

Support emitting analytical events from xblocks running in the LMS
parents ffd47c45 d6052675
......@@ -844,8 +844,8 @@ class CapaMixin(CapaFields):
score = self.lcp.get_score()
self.runtime.publish(
self,
'grade',
{
'event_name': 'grade',
'value': score['score'],
'max_value': score['total'],
}
......
......@@ -533,12 +533,12 @@ oauth_consumer_key="", oauth_signature="frVp4JuvT1mVXlxktiAUjQ7%2F1cw%3D"'}
if action == 'replaceResultRequest':
self.system.publish(
self,
'grade',
{
'event_name': 'grade',
'value': score * self.max_score(),
'max_value': self.max_score(),
},
custom_user=real_user
'user_id': real_user.id,
}
)
values = {
......
......@@ -1059,11 +1059,13 @@ class DescriptorSystem(ConfigurableFragmentWrapper, Runtime): # pylint: disable
"""
raise NotImplementedError("edX Platform doesn't currently implement XBlock resource urls")
def publish(self, block, event):
def publish(self, block, event_type, event):
"""
See :meth:`xblock.runtime.Runtime:publish` for documentation.
"""
raise NotImplementedError("edX Platform doesn't currently implement XBlock publish")
xmodule_runtime = getattr(block, 'xmodule_runtime', None)
if xmodule_runtime is not None:
return xmodule_runtime.publish(block, event_type, event)
def add_block_as_child_node(self, block, node):
child = etree.SubElement(node, "unknown")
......@@ -1228,7 +1230,7 @@ class ModuleSystem(ConfigurableFragmentWrapper, Runtime): # pylint: disable=abs
def resource_url(self, resource):
raise NotImplementedError("edX Platform doesn't currently implement XBlock resource urls")
def publish(self, block, event):
def publish(self, block, event_type, event):
pass
......
......@@ -21,8 +21,13 @@ These are properties and methods available on ``self.runtime`` when a view or ha
that the block is being executed in. The same student in two different courses
will have two different ids.
* publish(event): Emit events to the surrounding system. Events are dictionaries with
at least the key 'event_type', which identifies the other fields.
* publish(event): Emit events to the surrounding system. Events are dictionaries that can contain arbitrary data.
XBlocks can publish events by calling ``self.runtime.publish(self, event_type, event)``. The ``event_type`` parameter
enables downstream processing of the event since it uniquely identifies the schema. This call will cause the runtime
to save the event data in the application event stream. XBlocks should publish events whenever a significant state
change occurs. Post-hoc analysis of the event stream can yield insight about how the XBlock is used in the context of
the application. Ideally interesting state of the XBlock could be reconstructed at any point in history through
careful analysis of the event stream.
TODO: Link to the authoritive list of event types.
......@@ -51,12 +56,14 @@ should ``publish`` a ``grade`` event whenever the grade changes. The ``grade`` e
dictionary of the following form::
{
'event_type': 'grade',
'value': <number>,
'max_value': <number>,
'user_id': <number>,
}
The grade event represents a grade of ``value/max_value`` for the current user.
The grade event represents a grade of ``value/max_value`` for the current user. The
``user_id`` field is optional, the currently logged in user's ID will be used if it is
omitted.
Restrictions
~~~~~~~~~~~~
......
......@@ -292,15 +292,8 @@ def get_module_for_descriptor_internal(user, descriptor, field_data_cache, cours
position, wrap_xmodule_display, grade_bucket_type,
static_asset_path)
def publish(block, event, custom_user=None):
"""A function that allows XModules to publish events. This only supports grade changes right now."""
if event.get('event_name') != 'grade':
return
if custom_user:
user_id = custom_user.id
else:
user_id = user.id
def handle_grade_event(block, event_type, event):
user_id = event.get('user_id', user.id)
# Construct the key for the module
key = KeyValueStore.Key(
......@@ -333,6 +326,13 @@ def get_module_for_descriptor_internal(user, descriptor, field_data_cache, cours
dog_stats_api.increment("lms.courseware.question_answered", tags=tags)
def publish(block, event_type, event):
"""A function that allows XModules to publish events."""
if event_type == 'grade':
handle_grade_event(block, event_type, event)
else:
track_function(event_type, event)
# Build a list of wrapping functions that will be applied in order
# to the Fragment content coming out of the xblocks that are about to be rendered.
block_wrappers = []
......
......@@ -17,7 +17,7 @@
-e git+https://github.com/appliedsec/pygeoip.git@95e69341cebf5a6a9fbf7c4f5439d458898bdc3b#egg=pygeoip
# Our libraries:
-e git+https://github.com/edx/XBlock.git@6ec7edd6c44c7d2f1583df077cbf3e0f621ae984#egg=XBlock
-e git+https://github.com/edx/XBlock.git@cfe5c37f98febd9a215d23cb206a25711056a142#egg=XBlock
-e git+https://github.com/edx/codejail.git@e3d98f9455#egg=codejail
-e git+https://github.com/edx/diff-cover.git@v0.2.9#egg=diff_cover
-e git+https://github.com/edx/js-test-tool.git@v0.1.5#egg=js_test_tool
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment