Display warning message on course outline in Studio when course contains deprecated features/components
TNL-2303
This commit is contained in:
@@ -1,94 +0,0 @@
|
||||
"""
|
||||
API implementation of the Course Structure API for Python code.
|
||||
|
||||
Note: The course list and course detail functionality isn't currently supported here because
|
||||
of the tricky interactions between DRF and the code.
|
||||
Most of that information is available by accessing the course objects directly.
|
||||
"""
|
||||
|
||||
from course_structure_api.v0 import serializers
|
||||
from course_structure_api.v0.errors import CourseNotFoundError, CourseStructureNotAvailableError
|
||||
from openedx.core.djangoapps.content.course_structures import models, tasks
|
||||
from courseware import courses
|
||||
|
||||
|
||||
def _retrieve_course(course_key):
|
||||
"""Retrieves the course for the given course key.
|
||||
|
||||
Args:
|
||||
course_key: The CourseKey for the course we'd like to retrieve.
|
||||
Returns:
|
||||
the course that matches the CourseKey
|
||||
Raises:
|
||||
CourseNotFoundError
|
||||
|
||||
"""
|
||||
try:
|
||||
return courses.get_course(course_key)
|
||||
except ValueError:
|
||||
raise CourseNotFoundError
|
||||
|
||||
|
||||
def course_structure(course_key):
|
||||
"""
|
||||
Retrieves the entire course structure, including information about all the blocks used in the course.
|
||||
|
||||
Args:
|
||||
course_key: the CourseKey of the course we'd like to retrieve.
|
||||
Returns:
|
||||
The serialized output of the course structure:
|
||||
* root: The ID of the root node of the course structure.
|
||||
|
||||
* blocks: A dictionary that maps block IDs to a collection of
|
||||
information about each block. Each block contains the following
|
||||
fields.
|
||||
|
||||
* id: The ID of the block.
|
||||
|
||||
* type: The type of block. Possible values include sequential,
|
||||
vertical, html, problem, video, and discussion. The type can also be
|
||||
the name of a custom type of block used for the course.
|
||||
|
||||
* display_name: The display name configured for the block.
|
||||
|
||||
* graded: Whether or not the sequential or problem is graded. The
|
||||
value is true or false.
|
||||
|
||||
* format: The assignment type.
|
||||
|
||||
* children: If the block has child blocks, a list of IDs of the child
|
||||
blocks.
|
||||
Raises:
|
||||
CourseStructureNotAvailableError, CourseNotFoundError
|
||||
"""
|
||||
course = _retrieve_course(course_key)
|
||||
try:
|
||||
requested_course_structure = models.CourseStructure.objects.get(course_id=course.id)
|
||||
return serializers.CourseStructureSerializer(requested_course_structure.structure).data
|
||||
except models.CourseStructure.DoesNotExist:
|
||||
# If we don't have data stored, generate it and return an error.
|
||||
tasks.update_course_structure.delay(unicode(course_key))
|
||||
raise CourseStructureNotAvailableError
|
||||
|
||||
|
||||
def course_grading_policy(course_key):
|
||||
"""
|
||||
Retrieves the course grading policy.
|
||||
|
||||
Args:
|
||||
course_key: CourseKey the corresponds to the course we'd like to know grading policy information about.
|
||||
Returns:
|
||||
The serialized version of the course grading policy containing the following information:
|
||||
* assignment_type: The type of the assignment, as configured by course
|
||||
staff. For example, course staff might make the assignment types Homework,
|
||||
Quiz, and Exam.
|
||||
|
||||
* count: The number of assignments of the type.
|
||||
|
||||
* dropped: Number of assignments of the type that are dropped.
|
||||
|
||||
* weight: The weight, or effect, of the assignment type on the learner's
|
||||
final grade.
|
||||
"""
|
||||
course = _retrieve_course(course_key)
|
||||
return serializers.GradingPolicySerializer(course.raw_grader).data
|
||||
@@ -1,11 +0,0 @@
|
||||
""" Errors used by the Course Structure API. """
|
||||
|
||||
|
||||
class CourseNotFoundError(Exception):
|
||||
""" The course was not found. """
|
||||
pass
|
||||
|
||||
|
||||
class CourseStructureNotAvailableError(Exception):
|
||||
""" The course structure still needs to be generated. """
|
||||
pass
|
||||
@@ -40,37 +40,3 @@ class CourseSerializer(serializers.Serializer):
|
||||
def get_image_url(self, course):
|
||||
""" Get the course image URL """
|
||||
return course_image_url(course)
|
||||
|
||||
|
||||
class GradingPolicySerializer(serializers.Serializer):
|
||||
""" Serializer for course grading policy. """
|
||||
assignment_type = serializers.CharField(source='type')
|
||||
count = serializers.IntegerField(source='min_count')
|
||||
dropped = serializers.IntegerField(source='drop_count')
|
||||
weight = serializers.FloatField()
|
||||
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
class BlockSerializer(serializers.Serializer):
|
||||
""" Serializer for course structure block. """
|
||||
id = serializers.CharField(source='usage_key')
|
||||
type = serializers.CharField(source='block_type')
|
||||
display_name = serializers.CharField()
|
||||
graded = serializers.BooleanField(default=False)
|
||||
format = serializers.CharField()
|
||||
children = serializers.CharField()
|
||||
|
||||
|
||||
class CourseStructureSerializer(serializers.Serializer):
|
||||
""" Serializer for course structure. """
|
||||
root = serializers.CharField(source='root')
|
||||
blocks = serializers.SerializerMethodField('get_blocks')
|
||||
|
||||
def get_blocks(self, structure):
|
||||
""" Serialize the individual blocks. """
|
||||
serialized = {}
|
||||
|
||||
for key, block in structure['blocks'].iteritems():
|
||||
serialized[key] = BlockSerializer(block).data
|
||||
|
||||
return serialized
|
||||
|
||||
@@ -9,7 +9,6 @@ from mock import patch, Mock
|
||||
from itertools import product
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.test.utils import override_settings
|
||||
|
||||
from capa.tests.response_xml_factory import MultipleChoiceResponseXMLFactory
|
||||
from oauth2_provider.tests.factories import AccessTokenFactory, ClientFactory
|
||||
@@ -336,6 +335,7 @@ class CourseStructureTests(CourseDetailTestMixin, CourseViewTestsMixin, ModuleSt
|
||||
blocks[unicode(xblock.location)] = {
|
||||
u'id': unicode(xblock.location),
|
||||
u'type': xblock.category,
|
||||
u'parent': None,
|
||||
u'display_name': xblock.display_name,
|
||||
u'format': xblock.format,
|
||||
u'graded': xblock.graded,
|
||||
|
||||
@@ -15,14 +15,14 @@ from rest_framework.reverse import reverse
|
||||
from xmodule.modulestore.django import modulestore
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
|
||||
from course_structure_api.v0 import api, serializers
|
||||
from course_structure_api.v0.errors import CourseNotFoundError, CourseStructureNotAvailableError
|
||||
from course_structure_api.v0 import serializers
|
||||
from courseware import courses
|
||||
from courseware.access import has_access
|
||||
from courseware.model_data import FieldDataCache
|
||||
from courseware.module_render import get_module_for_descriptor
|
||||
from openedx.core.lib.api.view_utils import view_course_access, view_auth_classes
|
||||
from openedx.core.lib.api.serializers import PaginationSerializer
|
||||
from openedx.core.djangoapps.content.course_structures.api.v0 import api, errors
|
||||
from student.roles import CourseInstructorRole, CourseStaffRole
|
||||
from util.module_utils import get_dynamic_descriptor_children
|
||||
|
||||
@@ -73,7 +73,7 @@ class CourseViewMixin(object):
|
||||
self.course_key = CourseKey.from_string(course_id)
|
||||
self.check_course_permissions(self.request.user, self.course_key)
|
||||
return func(self, *args, **kwargs)
|
||||
except CourseNotFoundError:
|
||||
except errors.CourseNotFoundError:
|
||||
raise Http404
|
||||
|
||||
return func_wrapper
|
||||
@@ -262,7 +262,7 @@ class CourseStructure(CourseViewMixin, RetrieveAPIView):
|
||||
def get(self, request, **kwargs):
|
||||
try:
|
||||
return Response(api.course_structure(self.course_key))
|
||||
except CourseStructureNotAvailableError:
|
||||
except errors.CourseStructureNotAvailableError:
|
||||
# If we don't have data stored, we will try to regenerate it, so
|
||||
# return a 503 and as them to retry in 2 minutes.
|
||||
return Response(status=503, headers={'Retry-After': '120'})
|
||||
|
||||
Reference in New Issue
Block a user