fix: sort sections in course-optimizer before returning result (#36441)
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user