From 2b7e510eddd4c536c5874f206764288a47ddcbf7 Mon Sep 17 00:00:00 2001 From: nsprenkle Date: Fri, 24 May 2024 13:44:03 -0400 Subject: [PATCH] feat: add filter for hooking XBlock Render Started - chore: bump version to 1.9.0 --- CHANGELOG.rst | 8 +++ openedx_filters/__init__.py | 2 +- openedx_filters/learning/filters.py | 40 ++++++++++++ .../learning/tests/test_filters.py | 63 +++++++++++++++++++ 4 files changed, 112 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index a7b0800..c8dfd6e 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -14,6 +14,14 @@ Change Log Unreleased ---------- +[1.9.0] - 2024-06-14 +-------------------- + +Added +~~~~~~~ + +* RenderXBlockStarted filter added which allows reading / modifying of XBlock context prior to render. + [1.8.1] - 2024-04-12 -------------------- diff --git a/openedx_filters/__init__.py b/openedx_filters/__init__.py index b98bfe9..835c5d5 100644 --- a/openedx_filters/__init__.py +++ b/openedx_filters/__init__.py @@ -3,4 +3,4 @@ """ from openedx_filters.filters import * -__version__ = "1.8.1" +__version__ = "1.9.0" diff --git a/openedx_filters/learning/filters.py b/openedx_filters/learning/filters.py index ce28e2f..f95e4f8 100644 --- a/openedx_filters/learning/filters.py +++ b/openedx_filters/learning/filters.py @@ -546,6 +546,46 @@ def run_filter(cls, enrollments): return data.get("enrollments") +class RenderXBlockStarted(OpenEdxPublicFilter): + """ + Filter in between context generation and rendering of XBlock scope. + """ + + filter_type = "org.openedx.learning.xblock.render.started.v1" + + class PreventXBlockBlockRender(OpenEdxFilterException): + """ + Custom class used to prevent the XBlock from rendering for the user. + """ + + class RenderCustomResponse(OpenEdxFilterException): + """ + Custom class used to stop the XBlock rendering process and return a custom response. + """ + + def __init__(self, message, response=None): + """ + Override init that defines specific arguments used in the XBlock render process. + + Arguments: + message: error message for the exception. + response: custom response which will be returned by the XBlock render view. + """ + super().__init__(message, response=response) + + @classmethod + def run_filter(cls, context, student_view_context): + """ + Execute a filter with the specified signature. + + Arguments: + context (dict): rendering context values like is_mobile_app, show_title, etc. + student_view_context (dict): context passed to the student_view of the block context + """ + data = super().run_pipeline(context=context, student_view_context=student_view_context) + return data.get("context"), data.get("student_view_context") + + class VerticalBlockRenderCompleted(OpenEdxPublicFilter): """ Custom class used to create filters to act on vertical block rendering completed. diff --git a/openedx_filters/learning/tests/test_filters.py b/openedx_filters/learning/tests/test_filters.py index f872d5d..9021906 100644 --- a/openedx_filters/learning/tests/test_filters.py +++ b/openedx_filters/learning/tests/test_filters.py @@ -22,6 +22,7 @@ DashboardRenderStarted, InstructorDashboardRenderStarted, ORASubmissionViewRenderStarted, + RenderXBlockStarted, StudentLoginRequested, StudentRegistrationRequested, VerticalBlockChildRenderStarted, @@ -479,6 +480,68 @@ def test_halt_vertical_block_render(self, render_exception, attributes): self.assertDictContainsSubset(attributes, exception.__dict__) + def test_xblock_render_started(self): + """ + Test RenderXBlockStarted filter behavior under normal conditions. + + Expected behavior: + - The filter must have the signature specified. + - The filter should return the expected values + """ + context = { + "foo": False, + "bar": "baz", + "buzz": 1337, + } + student_view_context = { + "arbitrary_context": "value", + "more_arbitrary_context": True + } + + result = VerticalBlockChildRenderStarted.run_filter(context, student_view_context) + + self.assertTupleEqual((context, student_view_context), result) + + @data( + ( + RenderXBlockStarted.PreventXBlockBlockRender, + { + "message": "Danger, Will Robinson!" + } + ) + ) + @unpack + def test_halt_xblock_render(self, xblock_render_exception, attributes): + """ + Test for xblock render exception attributes. + + Expected behavior: + - The exception must have the attributes specified. + """ + exception = xblock_render_exception(**attributes) + + self.assertDictContainsSubset(attributes, exception.__dict__) + + @data( + ( + RenderXBlockStarted.RenderCustomResponse, + { + "message": "Danger, Will Robinson!" + } + ) + ) + @unpack + def test_halt_xblock_render_custom_response(self, xblock_render_exception, attributes): + """ + Test for xblock render exception attributes. + + Expected behavior: + - The exception must have the attributes specified. + """ + exception = xblock_render_exception(**attributes) + + self.assertDictContainsSubset(attributes, exception.__dict__) + def test_account_settings_render_started(self): """ Test AccountSettingsRenderStarted filter behavior under normal conditions.