fix: sort sections in course-optimizer before returning result (#36441)

This commit is contained in:
Hina Khadim
2025-03-28 19:34:29 +05:00
committed by GitHub
parent 1ca57ec129
commit 3834f20841
3 changed files with 102 additions and 3 deletions

View File

@@ -8,6 +8,8 @@ from user_tasks.models import UserTaskArtifact, UserTaskStatus
from cms.djangoapps.contentstore.tasks import CourseLinkCheckTask, LinkState
from cms.djangoapps.contentstore.xblock_storage_handlers.view_handlers import get_xblock
from cms.djangoapps.contentstore.xblock_storage_handlers.xblock_helpers import usage_key_with_run
from xmodule.modulestore import ModuleStoreEnum
from xmodule.modulestore.django import modulestore
# Restricts status in the REST API to only those which the requesting user has permission to view.
@@ -285,3 +287,26 @@ def _create_dto_recursive(xblock_node, xblock_dictionary):
xblock_children.append(xblock_entry)
return {level: xblock_children} if level else None
def sort_course_sections(course_key, data):
"""Retrieve and sort course sections based on the published course structure."""
course_blocks = modulestore().get_items(
course_key,
qualifiers={'category': 'course'},
revision=ModuleStoreEnum.RevisionOption.published_only
)
if not course_blocks or 'LinkCheckOutput' not in data or 'sections' not in data['LinkCheckOutput']:
return data # Return unchanged data if course_blocks or required keys are missing
sorted_section_ids = [section.location.block_id for section in course_blocks[0].get_children()]
sections_map = {section['id']: section for section in data['LinkCheckOutput']['sections']}
data['LinkCheckOutput']['sections'] = [
sections_map[section_id]
for section_id in sorted_section_ids
if section_id in sections_map
]
return data

View File

@@ -1,12 +1,14 @@
"""
Tests for course optimizer
"""
from unittest import mock
from unittest.mock import Mock
from cms.djangoapps.contentstore.tests.utils import CourseTestCase
from cms.djangoapps.contentstore.core.course_optimizer_provider import (
_update_node_tree_and_dictionary,
_create_dto_recursive
_create_dto_recursive,
sort_course_sections
)
from cms.djangoapps.contentstore.tasks import LinkState
@@ -222,3 +224,74 @@ class TestLinkCheckProvider(CourseTestCase):
expected = _create_dto_recursive(mock_node_tree, mock_dictionary)
self.assertEqual(expected_result, expected)
@mock.patch('cms.djangoapps.contentstore.core.course_optimizer_provider.modulestore', autospec=True)
def test_returns_unchanged_data_if_no_course_blocks(self, mock_modulestore):
"""Test that the function returns unchanged data if no course blocks exist."""
mock_modulestore_instance = Mock()
mock_modulestore.return_value = mock_modulestore_instance
mock_modulestore_instance.get_items.return_value = []
data = {}
result = sort_course_sections("course-v1:Test+Course", data)
assert result == data # Should return the original data
@mock.patch('cms.djangoapps.contentstore.core.course_optimizer_provider.modulestore', autospec=True)
def test_returns_unchanged_data_if_linkcheckoutput_missing(self, mock_modulestore):
"""Test that the function returns unchanged data if 'LinkCheckOutput' is missing."""
mock_modulestore_instance = Mock()
mock_modulestore.return_value = mock_modulestore_instance
data = {'LinkCheckStatus': 'Uninitiated'} # No 'LinkCheckOutput'
mock_modulestore_instance.get_items.return_value = data
result = sort_course_sections("course-v1:Test+Course", data)
assert result == data
@mock.patch('cms.djangoapps.contentstore.core.course_optimizer_provider.modulestore', autospec=True)
def test_returns_unchanged_data_if_sections_missing(self, mock_modulestore):
"""Test that the function returns unchanged data if 'sections' is missing."""
mock_modulestore_instance = Mock()
mock_modulestore.return_value = mock_modulestore_instance
data = {'LinkCheckStatus': 'Success', 'LinkCheckOutput': {}} # No 'LinkCheckOutput'
mock_modulestore_instance.get_items.return_value = data
result = sort_course_sections("course-v1:Test+Course", data)
assert result == data
@mock.patch('cms.djangoapps.contentstore.core.course_optimizer_provider.modulestore', autospec=True)
def test_sorts_sections_correctly(self, mock_modulestore):
"""Test that the function correctly sorts sections based on published course structure."""
mock_course_block = Mock()
mock_course_block.get_children.return_value = [
Mock(location=Mock(block_id="section2")),
Mock(location=Mock(block_id="section3")),
Mock(location=Mock(block_id="section1")),
]
mock_modulestore_instance = Mock()
mock_modulestore.return_value = mock_modulestore_instance
mock_modulestore_instance.get_items.return_value = [mock_course_block]
data = {
"LinkCheckOutput": {
"sections": [
{"id": "section1", "name": "Intro"},
{"id": "section2", "name": "Advanced"},
{"id": "section3", "name": "Bonus"}, # Not in course structure
]
}
}
result = sort_course_sections("course-v1:Test+Course", data)
expected_sections = [
{"id": "section2", "name": "Advanced"},
{"id": "section3", "name": "Bonus"},
{"id": "section1", "name": "Intro"},
]
assert result["LinkCheckOutput"]["sections"] == expected_sections

View File

@@ -6,7 +6,7 @@ from rest_framework.request import Request
from rest_framework.response import Response
from user_tasks.models import UserTaskStatus
from cms.djangoapps.contentstore.core.course_optimizer_provider import get_link_check_data
from cms.djangoapps.contentstore.core.course_optimizer_provider import get_link_check_data, sort_course_sections
from cms.djangoapps.contentstore.rest_api.v0.serializers.course_optimizer import LinkCheckSerializer
from cms.djangoapps.contentstore.tasks import check_broken_links
from common.djangoapps.student.auth import has_course_author_access, has_studio_read_access
@@ -139,6 +139,7 @@ class LinkCheckStatusView(DeveloperErrorViewMixin, APIView):
self.permission_denied(request)
data = get_link_check_data(request, course_id)
serializer = LinkCheckSerializer(data)
data = sort_course_sections(course_key, data)
serializer = LinkCheckSerializer(data)
return Response(serializer.data)