diff --git a/common/lib/xmodule/xmodule/capa_base.py b/common/lib/xmodule/xmodule/capa_base.py index edc0449af6cca4fbc44a1d5b382a1a2104b3cca9..b88f83f5523074e8496144e174f2b2e48f6c2f4b 100644 --- a/common/lib/xmodule/xmodule/capa_base.py +++ b/common/lib/xmodule/xmodule/capa_base.py @@ -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'], } diff --git a/common/lib/xmodule/xmodule/lti_module.py b/common/lib/xmodule/xmodule/lti_module.py index dc2fcf06b7b88cadd7f313ca13d3759f90b5f7b8..5e2bf900a778edd24ca09147635602a707750407 100644 --- a/common/lib/xmodule/xmodule/lti_module.py +++ b/common/lib/xmodule/xmodule/lti_module.py @@ -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 = { diff --git a/common/lib/xmodule/xmodule/x_module.py b/common/lib/xmodule/xmodule/x_module.py index 38bb8541523836f425569ef9ac384cf37498e631..1e3bebcc96e19a5df073f15dbf0f53ba9a8a56d2 100644 --- a/common/lib/xmodule/xmodule/x_module.py +++ b/common/lib/xmodule/xmodule/x_module.py @@ -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 diff --git a/docs/en_us/developers/source/xblocks.rst b/docs/en_us/developers/source/xblocks.rst index 7e7aee22f3b6eeb961bf356795af84e789435a8d..2a15ea53cb0c796e0932b49f9cdf3d46d99dd31e 100644 --- a/docs/en_us/developers/source/xblocks.rst +++ b/docs/en_us/developers/source/xblocks.rst @@ -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 ~~~~~~~~~~~~ diff --git a/lms/djangoapps/courseware/module_render.py b/lms/djangoapps/courseware/module_render.py index 4954a9bf4be2f3ce0b409d83a7a82f5ade0ad70f..9e6f246688d8dcb5573303cbacc22b939b635882 100644 --- a/lms/djangoapps/courseware/module_render.py +++ b/lms/djangoapps/courseware/module_render.py @@ -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 = [] diff --git a/requirements/edx/github.txt b/requirements/edx/github.txt index 9eda75f8a7f884259580e168ef7389ba77dde2ae..feca3fac5218223ee9cb0d32aa05e7984fee50ce 100644 --- a/requirements/edx/github.txt +++ b/requirements/edx/github.txt @@ -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