From 62f1d8e3fa90ccd9b41e1dfabdbe2cbc0805fc21 Mon Sep 17 00:00:00 2001 From: Aamish Baloch Date: Thu, 4 Jan 2018 15:16:57 +0500 Subject: [PATCH] YONK-852: Course Settings added to provide support to set language of a course Changes include the addition of Course Settings to provide support to set language of a course. Unit tests added also. --- .../migrations/0002_auto_20180105_0836.py | 34 ++++++++++++++ course_metadata/models.py | 19 +++++++- course_metadata/serializers.py | 25 ++++++++++ course_metadata/tests/tests.py | 47 +++++++++++++++++++ course_metadata/urls.py | 15 ++++++ course_metadata/views.py | 36 ++++++++++++++ setup.py | 2 +- 7 files changed, 176 insertions(+), 2 deletions(-) create mode 100644 course_metadata/migrations/0002_auto_20180105_0836.py create mode 100644 course_metadata/serializers.py create mode 100644 course_metadata/urls.py create mode 100644 course_metadata/views.py diff --git a/course_metadata/migrations/0002_auto_20180105_0836.py b/course_metadata/migrations/0002_auto_20180105_0836.py new file mode 100644 index 0000000..7a5639a --- /dev/null +++ b/course_metadata/migrations/0002_auto_20180105_0836.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models +import django.utils.timezone +import openedx.core.djangoapps.xmodule_django.models +import model_utils.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('course_metadata', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='CourseSetting', + fields=[ + ('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, verbose_name='created', editable=False)), + ('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, verbose_name='modified', editable=False)), + ('id', openedx.core.djangoapps.xmodule_django.models.CourseKeyField(max_length=255, serialize=False, primary_key=True)), + ('languages', models.TextField(help_text=b'A comma-separated list of language codes to release to the public.', blank=True)), + ], + options={ + 'abstract': False, + }, + ), + migrations.AlterField( + model_name='courseaggregatedmetadata', + name='id', + field=openedx.core.djangoapps.xmodule_django.models.CourseKeyField(max_length=255, serialize=False, primary_key=True), + ), + ] diff --git a/course_metadata/models.py b/course_metadata/models.py index 3f8136e..c166b88 100644 --- a/course_metadata/models.py +++ b/course_metadata/models.py @@ -1,6 +1,7 @@ """ Models for course_metadata app """ +import ast from django.db import models from model_utils.models import TimeStampedModel @@ -16,7 +17,7 @@ class CourseAggregatedMetaData(TimeStampedModel): This model contains aggregated metadata about a course such as total modules, total assessments. """ - id = CourseKeyField(db_index=True, primary_key=True, max_length=255) # pylint: disable=invalid-name + id = CourseKeyField(primary_key=True, max_length=255) # pylint: disable=invalid-name total_modules = models.IntegerField(default=0) total_assessments = models.IntegerField(default=0) @@ -42,3 +43,19 @@ def get_from_id(course_id): course_metadata.total_assessments = len(get_course_leaf_nodes(course_id)) course_metadata.save() return course_metadata + + +class CourseSetting(TimeStampedModel): + """ + This model have custom course settings. + """ + id = CourseKeyField(primary_key=True, max_length=255) + languages = models.TextField( + blank=True, + help_text="A comma-separated list of language codes to release to the public." + ) + + @property + def languages_list(self): + data = ast.literal_eval(self.languages) + return data diff --git a/course_metadata/serializers.py b/course_metadata/serializers.py new file mode 100644 index 0000000..dfcbafc --- /dev/null +++ b/course_metadata/serializers.py @@ -0,0 +1,25 @@ +import ast + +from rest_framework import serializers +from openedx.core.lib.api.serializers import CourseKeyField +from course_metadata.models import CourseSetting + + +class StringListField(serializers.ListField): + child = serializers.CharField(max_length=255) + + def to_representation(self, data): + """ + List of object instances -> List of dicts of primitive datatypes. + """ + if isinstance(data, basestring): + data = ast.literal_eval(data) + return data + + +class CourseSettingSerializer(serializers.ModelSerializer): + id = CourseKeyField(required=False) + languages = StringListField() + + class Meta: + model = CourseSetting diff --git a/course_metadata/tests/tests.py b/course_metadata/tests/tests.py index 6f7abeb..d317073 100644 --- a/course_metadata/tests/tests.py +++ b/course_metadata/tests/tests.py @@ -1,6 +1,8 @@ """ Tests for course_metadata app """ +from django.core.urlresolvers import reverse +from edx_solutions_api_integration.test_utils import APIClientMixin from mock_django import mock_signal_receiver from xmodule.modulestore.django import SignalHandler @@ -77,3 +79,48 @@ def test_get_course_aggregate_metadata_by_course_key(self): """ course_metadata = CourseAggregatedMetaData.get_from_id(self.course.id) self.assertEqual(course_metadata.total_assessments, 1) + + +class CourseSettingTests(ModuleStoreTestCase, APIClientMixin): + """ Test suite for Course Setting """ + + MODULESTORE = TEST_DATA_SPLIT_MODULESTORE + + def setUp(self): + super(CourseSettingTests, self).setUp() + + self.course = CourseFactory.create() + self.course_settings_uri = reverse('additional-course-settings', kwargs={'course_id': unicode(self.course.id)}) + self.languages = ["it", "de-at", "es", "pt-br"] + + def test_course_settings_languages(self): + """ + Test for setting/getting course languages from course settings + """ + data = {"languages": self.languages} + response = self.do_put(self.course_settings_uri, data) + self.assertEqual(response.status_code, 200) + + response = self.do_get(self.course_settings_uri) + self.assertEqual(response.status_code, 200) + for language in response.data['languages']: + self.assertIn(language, self.languages) + + def test_course_settings_update_languages(self): + """ + Test for updating course languages in course settings + """ + data = {"languages": self.languages} + response = self.do_put(self.course_settings_uri, data) + + self.assertEqual(response.status_code, 200) + for language in response.data['languages']: + self.assertIn(language, self.languages) + + updated_languages = ["it", "de-at", "es"] + data = {"languages": updated_languages} + response = self.do_put(self.course_settings_uri, data) + + self.assertEqual(response.status_code, 200) + for language in response.data['languages']: + self.assertIn(language, self.languages) diff --git a/course_metadata/urls.py b/course_metadata/urls.py new file mode 100644 index 0000000..2abd411 --- /dev/null +++ b/course_metadata/urls.py @@ -0,0 +1,15 @@ +from django.conf import settings +from django.conf.urls import patterns, url + +from . import views + +COURSE_ID_PATTERN = settings.COURSE_ID_PATTERN + +urlpatterns = patterns( + '', + url( + r'^{}/settings$'.format(COURSE_ID_PATTERN), + views.CourseSettingView.as_view(), + name='additional-course-settings', + ), +) diff --git a/course_metadata/views.py b/course_metadata/views.py new file mode 100644 index 0000000..f7aef84 --- /dev/null +++ b/course_metadata/views.py @@ -0,0 +1,36 @@ +from opaque_keys.edx.keys import CourseKey +from edx_solutions_api_integration.permissions import SecureRetrieveUpdateAPIView +from course_metadata.models import CourseSetting +from course_metadata.serializers import CourseSettingSerializer + + +class CourseSettingView(SecureRetrieveUpdateAPIView): + """ + **Use Case** + + Create course settings + + **Example Requests** + + POST /api/server/courses_metadata/{course_id}/settings + + **Request Params** + + **POST** + + -id: course id + -langauges: A comma-separated list of language codes. Example: ['it', 'de-at', 'es', 'pt-br'] + + **Response Values** + + **POST** + + If the request is successful, the request returns an HTTP 200 "OK" response. + """ + serializer_class = CourseSettingSerializer + queryset = CourseSetting.objects.all() + + def get_object(self): + course_id = CourseKey.from_string(self.kwargs['course_id']) + setting, __ = CourseSetting.objects.get_or_create(id=course_id) + return setting diff --git a/setup.py b/setup.py index 7f49b3d..6b5fd5a 100755 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ setup( name='course-edx-platform-extensions', - version='1.0.2', + version='1.0.3', description='Course metadata management extension for edX platform', long_description=open('README.rst').read(), author='edX',