Skip to content

ol_openedx_feedback: Per-block learner feedback in learning MFE#813

Merged
zamanafzal merged 8 commits into
mainfrom
zafzal/11629-block-feedback
Jul 22, 2026
Merged

ol_openedx_feedback: Per-block learner feedback in learning MFE#813
zamanafzal merged 8 commits into
mainfrom
zafzal/11629-block-feedback

Conversation

@zamanafzal

@zamanafzal zamanafzal commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

What are the relevant tickets?

Per-block content feedback — subtask mitodl/hq#11815 (RFC mitodl/hq#11812, parent mitodl/hq#11629).

Companion PRs:

  • mitodl/smoot-design#241 — feedback drawer UI bundle
  • mitodl/lehrer#83 — Learning MFE slot wiring
  • mitodl/mit-learn#3593 — backend endpoint + model

Description (What does it do?)

Adds ol_openedx_feedback, a lean trigger-only XBlockAside that renders a "Send feedback" megaphone on applicable blocks in the LMS student view. Feedback is not persisted in edx-platform — it is collected by the Learning MFE drawer and submitted to mit-learn.

  • Renders only for authenticated, non-author users; leaf blocks only (excludes course / chapter / sequential / vertical); gated per course by the ol_openedx_feedback.feedback_enabled CourseWaffleFlag.
  • On click it postMessages ol-feedback::drawer-open to the parent (Learning MFE) window with block context (courseId, blockUsageKey, blockType, blockDisplayName).
  • Trigger-only — no models, serializers, views, URLs, or migrations; apps.py is aside-only (plugin_app = {}). The MFE owns the submit URL and enriches the payload with course_name / unit_title / url.

Screenshot:

Audit Mode:
Screenshot 2026-07-21 at 4 36 00 PM

Staff Mode:
Screenshot 2026-07-21 at 4 36 00 PM

How can this be tested?

  • Install the plugin in the LMS and enable the ol_openedx_feedback.feedback_enabled CourseWaffleFlag (globally or per course).
  • Open a courseware unit as a signed-in learner → the "Send feedback" megaphone renders on leaf blocks (docked next to AskTIM when present); clicking it opens the feedback drawer in the Learning MFE.
  • Automated: pytest src/ol_openedx_feedback/tests — covers should_apply_to_block (waffle-flag gate + leaf-block filter + course-import branch) and student-view rendering (authenticated vs anonymous/author).

Additional Context

The drawer UI ships in smoot-design (#241), the MFE slot wiring in lehrer (#83), and submissions are stored by mit-learn (#3593). This PR is the LMS-side trigger only.

@zamanafzal zamanafzal changed the title refactor(ol_openedx_feedback): strip to aside-only (data -> learn-ai) ol_openedx_feedback: Per-block learner feedback in learning MFE Jun 15, 2026
Comment thread src/ol_openedx_feedback/ol_openedx_feedback/static/js/feedback.js Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new ol_openedx_feedback Open edX plugin that registers an XBlockAside to render a per-block “Send feedback” trigger in LMS student view and forward block context to the Learning MFE via postMessage, gated by a course waffle flag.

Changes:

  • Introduces the FeedbackAside XBlockAside (plus waffle-flag compat helper) to gate and render a lightweight trigger-only UI on leaf blocks.
  • Adds frontend assets (HTML/CSS/JS) for the trigger and its interaction/placement behavior alongside AskTIM when present.
  • Adds pytest coverage and shared test utilities for applicability gating and student-view rendering behavior.

Reviewed changes

Copilot reviewed 15 out of 17 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/ol_openedx_feedback/pyproject.toml New plugin package metadata + entry points for the aside and Django app registration.
src/ol_openedx_feedback/README.rst Documents trigger behavior, message contract, and waffle-flag enablement.
src/ol_openedx_feedback/setup.cfg Plugin-local pytest/isort configuration mirroring other plugins.
src/ol_openedx_feedback/ol_openedx_feedback/init.py Initializes the Python package.
src/ol_openedx_feedback/ol_openedx_feedback/apps.py Declares an aside-only Django AppConfig (plugin_app = {}).
src/ol_openedx_feedback/ol_openedx_feedback/constants.py Defines container block types excluded from applicability.
src/ol_openedx_feedback/ol_openedx_feedback/utils.py Adds helper to determine whether a block is eligible (leaf vs container).
src/ol_openedx_feedback/ol_openedx_feedback/compat.py Encapsulates CourseWaffleFlag wiring for ol_openedx_feedback.feedback_enabled.
src/ol_openedx_feedback/ol_openedx_feedback/block.py Implements the FeedbackAside rendering + waffle-gated should_apply_to_block.
src/ol_openedx_feedback/ol_openedx_feedback/static/html/student_view.html Trigger markup (button + inline icon) with accessibility attributes.
src/ol_openedx_feedback/ol_openedx_feedback/static/css/feedback.css Styling for trigger placement and focus/hover states.
src/ol_openedx_feedback/ol_openedx_feedback/static/js/feedback.js JS to emit ol-feedback::drawer-open and optionally dock beside AskTIM.
src/ol_openedx_feedback/tests/init.py Marks tests as a package.
src/ol_openedx_feedback/tests/conftest.py Adds logging-control pytest CLI options (consistent with other plugins).
src/ol_openedx_feedback/tests/utils.py Shared ModuleStore-backed test base that builds a course tree + aside instances.
src/ol_openedx_feedback/tests/test_utils.py Unit tests for leaf/container applicability helper.
src/ol_openedx_feedback/tests/test_aside.py Tests for rendering gates and should_apply_to_block waffle/import behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/ol_openedx_feedback/ol_openedx_feedback/static/js/feedback.js Outdated
Comment thread src/ol_openedx_feedback/ol_openedx_feedback/static/js/feedback.js Outdated
@zamanafzal
zamanafzal force-pushed the zafzal/11629-block-feedback branch from f922b23 to 5fc18ff Compare July 14, 2026 12:46
@asadali145
asadali145 self-requested a review July 14, 2026 13:25
@asadali145 asadali145 self-assigned this Jul 14, 2026

@asadali145 asadali145 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should add the plugin metadata in AGENTS.md. Also, I have a few suggestions.

Comment on lines +46 to +49
if getattr(self.runtime, "is_author_mode", False):
return fragment
if not getattr(self.runtime, "user_id", None):
return fragment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if getattr(self.runtime, "is_author_mode", False):
return fragment
if not getattr(self.runtime, "user_id", None):
return fragment
if getattr(self.runtime, "is_author_mode", False) or not getattr(self.runtime, "user_id", None):
return fragment

Comment on lines +86 to +87
if isinstance(block.runtime, XMLImportingModuleStoreRuntime):
return is_aside_applicable_to_block(block)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need this check in this Aside, it was required for ol_openedx_chat because of the checks it applied in is_aside_applicable_to_block.

Comment on lines +12 to +26
def test_leaf_blocks_are_applicable():
assert is_aside_applicable_to_block(_block("video")) is True
assert is_aside_applicable_to_block(_block("problem")) is True
assert is_aside_applicable_to_block(_block("html")) is True


def test_container_blocks_are_excluded():
assert is_aside_applicable_to_block(_block("vertical")) is False
assert is_aside_applicable_to_block(_block("sequential")) is False
assert is_aside_applicable_to_block(_block("chapter")) is False
assert is_aside_applicable_to_block(_block("course")) is False


def test_missing_category_is_not_applicable():
assert is_aside_applicable_to_block(_block(None)) is False

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can be a single parametrized test with the block name and expected output.

return Template(_resource(path)).render(Context(context))


class FeedbackAside(XBlockAside):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't disable feedback for a block from the authoring MFE, and also, we cannot disable it for a block type like text block, etc.

I think we should add the ability to disable feedback for a block from authoring and convert the constant to a setting to be able to disable it for a block type.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We intentionally left this out as per the direction on the parent issue. Feedback should be "enabled for all blocks"

Comment thread src/ol_openedx_feedback/README.rst
Comment thread src/ol_openedx_feedback/ol_openedx_feedback/constants.py Outdated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to add setup/installation steps.

@zamanafzal
zamanafzal force-pushed the zafzal/11629-block-feedback branch 2 times, most recently from e6b5f21 to 7ff2ba0 Compare July 16, 2026 10:00
Comment thread src/ol_openedx_feedback/ol_openedx_feedback/static/js/feedback.js
@zamanafzal
zamanafzal force-pushed the zafzal/11629-block-feedback branch 2 times, most recently from 8560050 to 1dcf4ac Compare July 16, 2026 10:18
Comment thread src/ol_openedx_feedback/ol_openedx_feedback/block.py

@asadali145 asadali145 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good overall in testing, we should update the screenshot in PR. Also, an issue that we discussed over call is that staff debug button is hidden when we enable the feedback and chat aside at a time.

@zamanafzal

Copy link
Copy Markdown
Contributor Author

@asadali145 I've made the changes. Please take a look.

- Style the trigger as a button (border + background) whether standalone or
  docked next to AskTIM, with a full-cover grey + red border on click
- Mask the problem-block "saved/submit" notification line across the gap to
  the AskTIM button (JS flags problem blocks via payload.blockType)
Reinstate the XMLImportingModuleStoreRuntime guard so should_apply_to_block
skips the waffle-flag / context_key lookup during course import (when the
block has no resolvable course context) and gates on block type only. Restore
the import-path test cases. Addresses the Sentry review finding.
The feedback trigger renders only in the learner (student) view, so the plugin
does not need to load in Studio/CMS. Drop the cms.djangoapp entry point and the
ProjectType.CMS settings registration; the aside still evaluates during course
import via the xblock_asides.v1 entry point (guarded), and the excluded-block-
types setting falls back to its default there.
@zamanafzal
zamanafzal force-pushed the zafzal/11629-block-feedback branch from 34d95ac to 995441f Compare July 21, 2026 11:41
Handle the AskTIM chat container's whitesmoke box-shadow that covers the
platform "Staff Debug Info" link on video blocks within feedback.css, so
the fix holds independent of the installed ol_openedx_chat version. Give
the staff-info row its own stacking context and matching background,
scoped to video blocks only.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

@asadali145 asadali145 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like there is some issue with problem block styles now.

We should check why we didn't need the changes to make staff debug visible in chat aside.

Image

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please add a step in installation guide to enable the asides from LMS admin?

zamanafzal and others added 2 commits July 21, 2026 17:40
The docked line-mask box-shadow projected 12px of white but the gap to
the AskTIM button is only 10px, so it painted over the button's left
border. Match the offset to the gap so the mask fills it without overlap.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Asides only render when XBlockAsidesConfig is enabled (off by default),
so document enabling it and checking the disabled_blocks list as part of
installation.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

@asadali145 asadali145 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

There is a slight room for improvement for Mobile and tablet design but we can revisit it later. Also, we will create an issue to do R&D to improve/ease styling of asides.

@zamanafzal
zamanafzal merged commit c296b4a into main Jul 22, 2026
7 of 10 checks passed
@zamanafzal
zamanafzal deleted the zafzal/11629-block-feedback branch July 22, 2026 07:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants