feat: Improve robust score rendering with event-based architecture

This commit implements a comprehensive solution for test score integration in the
enhancement system along with improvements to the score rendering mechanism. Key
changes include:

- Add event handler for rendering blocks with edx-submissions scores
- Implement event-based mechanism to render XBlocks with scoring data
- Create signal handlers in handlers.py to process external grader scores
- Develop specialized XBlock loader for rendering without HTTP requests
- Add queue_key propagation across the submission pipeline
- Register submission URLs in LMS routing configuration
- Add complete docstrings to score render module for better code maintainability
- Add ADR for XBlock rendering with external grader integration
- Add openedx-events fork branch as a dependency in testing.in
- Upgrade edx submission dependency

These changes support the migration from traditional XQueue callback HTTP requests
to a more robust event-based architecture, improving performance and reliability
when processing submission scores. The included ADR documents the architectural
decision and implementation approach for this significant improvement to the
external grading workflow.
This commit is contained in:
Leonardo Beroes
2025-04-14 08:28:28 -04:00
committed by David Ormsbee
parent e1747f3844
commit 70ea641c99
15 changed files with 790 additions and 51 deletions

View File

@@ -90,26 +90,23 @@ def test_send_to_queue_with_flag_enabled(mock_send_to_submission, mock_flag):
block = Mock() # Mock block for the constructor
xqueue_interface = XQueueInterface(url, django_auth, block=block)
header = json.dumps(
{
"lms_callback_url": (
"http://example.com/courses/course-v1:test_org+test_course+test_run/"
"xqueue/block@item_id/type@problem"
),
}
)
body = json.dumps(
{
"student_info": json.dumps({"anonymous_student_id": "student_id"}),
"student_response": "student_answer",
}
)
header = json.dumps({
"lms_callback_url": (
"http://example.com/courses/course-v1:test_org+test_course+test_run/"
"xqueue/block@item_id/type@problem"
),
"lms_key": "default"
})
body = json.dumps({
"student_info": json.dumps({"anonymous_student_id": "student_id"}),
"student_response": "student_answer",
})
files_to_upload = None
mock_send_to_submission.return_value = {"submission": "mock_submission"}
error, msg = xqueue_interface.send_to_queue(header, body, files_to_upload)
mock_send_to_submission.assert_called_once_with(header, body, {})
mock_send_to_submission.assert_called_once_with(header, body, "default", {})
@pytest.mark.django_db
@@ -122,20 +119,17 @@ def test_send_to_queue_with_flag_disabled(mock_http_post, mock_flag):
block = Mock() # Mock block for the constructor
xqueue_interface = XQueueInterface(url, django_auth, block=block)
header = json.dumps(
{
"lms_callback_url": (
"http://example.com/courses/course-v1:test_org+test_course+test_run/"
"xqueue/block@item_id/type@problem"
),
}
)
body = json.dumps(
{
"student_info": json.dumps({"anonymous_student_id": "student_id"}),
"student_response": "student_answer",
}
)
header = json.dumps({
"lms_callback_url": (
"http://example.com/courses/course-v1:test_org+test_course+test_run/"
"xqueue/block@item_id/type@problem"
),
"lms_key": "default"
})
body = json.dumps({
"student_info": json.dumps({"anonymous_student_id": "student_id"}),
"student_response": "student_answer",
})
files_to_upload = None
mock_http_post.return_value = (0, "Submission sent successfully")