feat: Add support for legacy discussions settings to API

This commit is contained in:
stvn
2021-04-06 09:34:00 -07:00
parent 4776ab1504
commit eaeec735a9
2 changed files with 120 additions and 11 deletions

View File

@@ -6,7 +6,6 @@ import unittest
from django.conf import settings
from django.urls import reverse
from opaque_keys.edx.keys import CourseKey
from rest_framework import status
from rest_framework.test import APITestCase

View File

@@ -13,6 +13,8 @@ from rest_framework.views import APIView
from common.djangoapps.student.roles import CourseStaffRole
from openedx.core.lib.api.authentication import BearerAuthenticationAllowInactiveUser
from openedx.core.lib.courses import get_course_by_id
from xmodule.modulestore.django import modulestore
from .models import DEFAULT_PROVIDER_TYPE
from .models import DiscussionsConfiguration
@@ -79,6 +81,66 @@ class LtiSerializer(serializers.ModelSerializer):
return instance
class LegacySettingsSerializer(serializers.BaseSerializer):
"""
Serialize legacy discussions settings
"""
class Meta:
fields = [
'allow_anonymous',
'allow_anonymous_to_peers',
'discussion_blackouts',
'discussion_topics',
# The following fields are deprecated;
# they technically still exist in Studio (so we mention them here),
# but they are not supported in the new experience:
# 'discussion_link',
# 'discussion_sort_alpha',
]
def create(self, validated_data):
"""
We do not need this.
"""
raise NotImplementedError
def to_internal_value(self, data: dict) -> dict:
"""
Transform the incoming primitive data into a native value
"""
payload = {
key: value
for key, value in data.items()
if key in self.Meta.fields
}
return payload
def to_representation(self, instance) -> dict:
"""
Serialize data into a dictionary, to be used as a response
"""
settings = {
field.name: field.read_json(instance)
for field in instance.fields.values()
if field.name in self.Meta.fields
}
return settings
def update(self, instance, validated_data: dict):
"""
Update and save an existing instance
"""
save = False
for field in self.Meta.fields:
if field in validated_data:
value = validated_data[field]
setattr(instance, field, value)
save = True
if save:
modulestore().update_item(instance, self.context['user_id'])
return instance
class DiscussionsConfigurationView(APIView):
"""
Handle configuration-related view-logic
@@ -97,11 +159,16 @@ class DiscussionsConfigurationView(APIView):
class Meta:
model = DiscussionsConfiguration
fields = [
'context_key',
'enabled',
'provider_type',
]
def create(self, validated_data):
"""
We do not need this.
"""
raise NotImplementedError
def to_internal_value(self, data: dict) -> dict:
"""
Transform the *incoming* primitive data into a native value.
@@ -125,6 +192,17 @@ class DiscussionsConfigurationView(APIView):
if supports_lti:
lti_configuration = LtiSerializer(instance.lti_configuration)
lti_configuration_data = lti_configuration.data
provider_type = instance.provider_type or DEFAULT_PROVIDER_TYPE
plugin_configuration = instance.plugin_configuration
if provider_type == 'legacy':
course_key = instance.context_key
course = get_course_by_id(course_key)
legacy_settings = LegacySettingsSerializer(
course,
data=plugin_configuration,
)
if legacy_settings.is_valid(raise_exception=True):
plugin_configuration = legacy_settings.data
payload.update({
'features': {
'discussion-page',
@@ -133,9 +211,9 @@ class DiscussionsConfigurationView(APIView):
'wcag-2.1',
},
'lti_configuration': lti_configuration_data,
'plugin_configuration': instance.plugin_configuration,
'plugin_configuration': plugin_configuration,
'providers': {
'active': instance.provider_type or DEFAULT_PROVIDER_TYPE,
'active': provider_type or DEFAULT_PROVIDER_TYPE,
'available': PROVIDER_FEATURE_MAP,
},
})
@@ -145,18 +223,14 @@ class DiscussionsConfigurationView(APIView):
"""
Update and save an existing instance
"""
keys = [
'enabled',
'plugin_configuration',
'provider_type',
]
for key in keys:
for key in self.Meta.fields:
value = validated_data.get(key)
if value is not None:
setattr(instance, key, value)
# _update_* helpers assume `enabled` and `provider_type`
# have already been set
instance = self._update_lti(instance, validated_data)
instance = self._update_plugin_configuration(instance, validated_data)
instance.save()
return instance
@@ -180,6 +254,35 @@ class DiscussionsConfigurationView(APIView):
instance.lti_configuration = lti_configuration
return instance
def _update_plugin_configuration(
self,
instance: DiscussionsConfiguration,
validated_data: dict,
) -> DiscussionsConfiguration:
"""
Create/update legacy provider settings
"""
updated_provider_type = validated_data.get('provider_type') or instance.provider_type
will_support_legacy = bool(
updated_provider_type == 'legacy'
)
if will_support_legacy:
course_key = instance.context_key
course = get_course_by_id(course_key)
legacy_settings = LegacySettingsSerializer(
course,
context={
'user_id': self.context['user_id'],
},
data=validated_data.get('plugin_configuration', {}),
)
if legacy_settings.is_valid(raise_exception=True):
legacy_settings.save()
instance.plugin_configuration = {}
else:
instance.plugin_configuration = validated_data.get('plugin_configuration') or {}
return instance
# pylint: disable=redefined-builtin
def get(self, request, course_key_string: str, **_kwargs) -> Response:
"""
@@ -198,7 +301,14 @@ class DiscussionsConfigurationView(APIView):
"""
course_key = _validate_course_key(course_key_string)
configuration = DiscussionsConfiguration.get(course_key)
serializer = self.Serializer(configuration, data=request.data, partial=True)
serializer = self.Serializer(
configuration,
context={
'user_id': request.user.id,
},
data=request.data,
partial=True,
)
if serializer.is_valid(raise_exception=True):
serializer.save()
return Response(serializer.data)