Skip to content

Commit

Permalink
Merge pull request #3 from edx/master
Browse files Browse the repository at this point in the history
Update the repo
  • Loading branch information
amitvadhel authored Sep 2, 2019
2 parents af59a7c + dd26f3b commit 4c01e20
Show file tree
Hide file tree
Showing 1,817 changed files with 72,355 additions and 39,336 deletions.
4 changes: 2 additions & 2 deletions .coveragerc
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# .coveragerc for edx-platform
[run]
data_file = reports/.coverage
data_file = reports/${TEST_SUITE}.coverage
source =
cms
common/djangoapps
Expand Down Expand Up @@ -49,4 +49,4 @@ output = reports/coverage.xml
jenkins_source =
/home/jenkins/workspace/$JOB_NAME
/home/jenkins/workspace/$SUBSET_JOB
/edx/app/edxapp/edx-platform
/home/jenkins/edx-platform
2 changes: 1 addition & 1 deletion .github/renovate.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"schedule:weekdays",
":preserveSemverRanges"
],
"prConcurrentLimit": 2,
"prConcurrentLimit": 5,
"includePaths": [
"package.json"
]
Expand Down
7 changes: 0 additions & 7 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -143,10 +143,3 @@ dist

# Locally generated PII reports
pii_report

# Local documentation builds
docs/_build
docs/cms
docs/common
docs/lms
docs/openedx
3 changes: 1 addition & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ clean: ## archive and delete most git-ignored files
rm $(PRIVATE_FILES)

docs: ## build the developer documentation for this repository
rm -rf docs/_build docs/cms docs/common docs/lms docs/openedx
cd docs; make html
cd docs/guides; make clean html

extract_translations: ## extract localizable strings from sources
i18n_tool extract -v
Expand Down
37 changes: 26 additions & 11 deletions cms/djangoapps/api/v1/serializers/course_runs.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
""" Course run serializers. """
from __future__ import absolute_import

import logging
import time
import time # pylint: disable=unused-import

import six
from django.contrib.auth import get_user_model
from django.db import transaction
from django.utils.translation import ugettext_lazy as _
from opaque_keys import InvalidKeyError
from rest_framework import serializers
from rest_framework.fields import empty

Expand Down Expand Up @@ -82,7 +86,7 @@ def update_team(self, instance, team):

def image_is_jpeg_or_png(value):
content_type = value.content_type
if content_type not in IMAGE_TYPES.keys():
if content_type not in list(IMAGE_TYPES.keys()):
raise serializers.ValidationError(
u'Only JPEG and PNG image types are supported. {} is not valid'.format(content_type))

Expand Down Expand Up @@ -125,7 +129,7 @@ def update(self, instance, validated_data):
class CourseRunSerializerCommonFieldsMixin(serializers.Serializer):
schedule = CourseRunScheduleSerializer(source='*', required=False)
pacing_type = CourseRunPacingTypeField(source='self_paced', required=False,
choices=(('instructor_paced', False), ('self_paced', True),))
choices=((False, 'instructor_paced'), (True, 'self_paced'),))


class CourseRunSerializer(CourseRunSerializerCommonFieldsMixin, CourseRunTeamSerializerMixin, serializers.Serializer):
Expand Down Expand Up @@ -165,29 +169,40 @@ def create(self, validated_data):
class CourseRunRerunSerializer(CourseRunSerializerCommonFieldsMixin, CourseRunTeamSerializerMixin,
serializers.Serializer):
title = serializers.CharField(source='display_name', required=False)
number = serializers.CharField(source='id.course', required=False)
run = serializers.CharField(source='id.run')

def validate_run(self, value):
def validate(self, attrs):
course_run_key = self.instance.id
_id = attrs.get('id')
number = _id.get('course', course_run_key.course)
run = _id['run']
store = modulestore()
with store.default_store('split'):
new_course_run_key = store.make_course_key(course_run_key.org, course_run_key.course, value)
try:
with store.default_store('split'):
new_course_run_key = store.make_course_key(course_run_key.org, number, run)
except InvalidKeyError:
raise serializers.ValidationError(
u'Invalid key supplied. Ensure there are no special characters in the Course Number.'
)
if store.has_course(new_course_run_key, ignore_case=True):
raise serializers.ValidationError(u'Course run {key} already exists'.format(key=new_course_run_key))
return value
raise serializers.ValidationError(
{'run': u'Course run {key} already exists'.format(key=new_course_run_key)}
)
return attrs

def update(self, instance, validated_data):
course_run_key = instance.id
_id = validated_data.pop('id')
number = _id.get('course', course_run_key.course)
run = _id['run']
team = validated_data.pop('team', [])
user = self.context['request'].user
fields = {
'display_name': instance.display_name
}
fields.update(validated_data)
new_course_run_key = rerun_course(
user, course_run_key, course_run_key.org, course_run_key.course, _id['run'], fields, False
)
new_course_run_key = rerun_course(user, course_run_key, course_run_key.org, number, run, fields, False)

course_run = get_course_and_check_access(new_course_run_key, user)
self.update_team(course_run, team)
Expand Down
11 changes: 8 additions & 3 deletions cms/djangoapps/api/v1/tests/test_serializers/test_course_runs.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
"""Tests for course run serializers"""

from __future__ import absolute_import

import datetime

import ddt
import pytz
from django.test import RequestFactory
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory

from openedx.core.lib.courses import course_image_url
from student.roles import CourseInstructorRole, CourseStaffRole
from student.tests.factories import UserFactory
from ..utils import serialize_datetime
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory

from ...serializers.course_runs import CourseRunSerializer
from ..utils import serialize_datetime


@ddt.ddt
Expand Down
61 changes: 51 additions & 10 deletions cms/djangoapps/api/v1/tests/test_views/test_course_runs.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,31 @@
"""Tests for Course run views"""

from __future__ import absolute_import

import datetime

import ddt
import pytz
from django.core.files.uploadedfile import SimpleUploadedFile
from django.urls import reverse
from django.test import RequestFactory
from django.urls import reverse
from mock import patch
from opaque_keys.edx.keys import CourseKey
from openedx.core.lib.courses import course_image_url
from rest_framework.test import APIClient

from openedx.core.lib.courses import course_image_url
from student.models import CourseAccessRole
from student.tests.factories import TEST_PASSWORD, AdminFactory, UserFactory
from util.organizations_helpers import add_organization, get_course_organizations
from xmodule.contentstore.content import StaticContent
from xmodule.contentstore.django import contentstore
from xmodule.exceptions import NotFoundError
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory, ToyCourseFactory

from student.models import CourseAccessRole
from student.tests.factories import AdminFactory, TEST_PASSWORD, UserFactory
from ..utils import serialize_datetime
from ...serializers.course_runs import CourseRunSerializer
from ..utils import serialize_datetime


@ddt.ddt
Expand Down Expand Up @@ -315,19 +322,25 @@ def test_images_upload(self):
# There should now be an image stored
contentstore().find(content_key)

@patch.dict('django.conf.settings.FEATURES', {'ORGANIZATIONS_APP': True})
@ddt.data(
('instructor_paced', False),
('self_paced', True),
('instructor_paced', False, 'NotOriginalNumber1x'),
('self_paced', True, None),
)
@ddt.unpack
def test_rerun(self, pacing_type, expected_self_paced_value):
course_run = ToyCourseFactory()
def test_rerun(self, pacing_type, expected_self_paced_value, number):
original_course_run = ToyCourseFactory()
add_organization({
'name': 'Test Organization',
'short_name': original_course_run.id.org,
'description': 'Testing Organization Description',
})
start = datetime.datetime.now(pytz.UTC).replace(microsecond=0)
end = start + datetime.timedelta(days=30)
user = UserFactory()
role = 'instructor'
run = '3T2017'
url = reverse('api:v1:course_run-rerun', kwargs={'pk': str(course_run.id)})
url = reverse('api:v1:course_run-rerun', kwargs={'pk': str(original_course_run.id)})
data = {
'run': run,
'schedule': {
Expand All @@ -342,16 +355,31 @@ def test_rerun(self, pacing_type, expected_self_paced_value):
],
'pacing_type': pacing_type,
}
# If number is supplied, this should become the course number used in the course run key
# If not, it should default to the original course run number that the rerun is based on.
if number:
data.update({'number': number})
response = self.client.post(url, data, format='json')
assert response.status_code == 201

course_run_key = CourseKey.from_string(response.data['id'])
course_run = modulestore().get_course(course_run_key)

assert course_run.id.run == run
assert course_run.self_paced is expected_self_paced_value

if number:
assert course_run.id.course == number
assert course_run.id.course != original_course_run.id.course
else:
assert course_run.id.course == original_course_run.id.course

self.assert_course_run_schedule(course_run, start, end)
self.assert_access_role(course_run, user, role)
self.assert_course_access_role_count(course_run, 1)
course_orgs = get_course_organizations(course_run_key)
self.assertEqual(len(course_orgs), 1)
self.assertEqual(course_orgs[0]['short_name'], original_course_run.id.org)

def test_rerun_duplicate_run(self):
course_run = ToyCourseFactory()
Expand All @@ -362,3 +390,16 @@ def test_rerun_duplicate_run(self):
response = self.client.post(url, data, format='json')
assert response.status_code == 400
assert response.data == {'run': [u'Course run {key} already exists'.format(key=course_run.id)]}

def test_rerun_invalid_number(self):
course_run = ToyCourseFactory()
url = reverse('api:v1:course_run-rerun', kwargs={'pk': str(course_run.id)})
data = {
'run': '2T2019',
'number': '!@#$%^&*()',
}
response = self.client.post(url, data, format='json')
assert response.status_code == 400
assert response.data == {'non_field_errors': [
u'Invalid key supplied. Ensure there are no special characters in the Course Number.'
]}
2 changes: 2 additions & 0 deletions cms/djangoapps/cms_user_tasks/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
Signal handlers are connected here.
"""

from __future__ import absolute_import

from django.apps import AppConfig


Expand Down
2 changes: 2 additions & 0 deletions cms/djangoapps/cms_user_tasks/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
Celery tasks used by cms_user_tasks
"""

from __future__ import absolute_import

from boto.exception import NoAuthHandlerFound
from celery.exceptions import MaxRetriesExceededError
from celery.task import task
Expand Down
6 changes: 3 additions & 3 deletions cms/djangoapps/contentstore/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
Admin site bindings for contentstore
"""

from __future__ import absolute_import

from config_models.admin import ConfigurationModelAdmin
from django.contrib import admin

from contentstore.models import PushNotificationConfig, VideoUploadConfig

from contentstore.models import VideoUploadConfig

admin.site.register(VideoUploadConfig, ConfigurationModelAdmin)
admin.site.register(PushNotificationConfig, ConfigurationModelAdmin)
2 changes: 2 additions & 0 deletions cms/djangoapps/contentstore/api/tests/base.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
"""
Base test case for the course API views.
"""
from __future__ import absolute_import

from django.core.urlresolvers import reverse
from rest_framework.test import APITestCase

Expand Down
4 changes: 3 additions & 1 deletion cms/djangoapps/contentstore/api/tests/test_import.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
"""
Tests for the course import API views
"""
from __future__ import absolute_import

import os
import tarfile
import tempfile
Expand All @@ -9,10 +11,10 @@
from path import Path as path
from rest_framework import status
from rest_framework.test import APITestCase
from user_tasks.models import UserTaskStatus

from lms.djangoapps.courseware.tests.factories import StaffFactory
from student.tests.factories import UserFactory
from user_tasks.models import UserTaskStatus
from xmodule.modulestore.tests.django_utils import TEST_DATA_SPLIT_MODULESTORE, SharedModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory

Expand Down
2 changes: 2 additions & 0 deletions cms/djangoapps/contentstore/api/tests/test_quality.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
"""
Tests for the course import API views
"""
from __future__ import absolute_import

from rest_framework import status

from .base import BaseCourseViewTest
Expand Down
3 changes: 3 additions & 0 deletions cms/djangoapps/contentstore/api/tests/test_validation.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
"""
Tests for the course import API views
"""
from __future__ import absolute_import

from datetime import datetime

from django.core.urlresolvers import reverse
from rest_framework import status
from rest_framework.test import APITestCase
Expand Down
3 changes: 2 additions & 1 deletion cms/djangoapps/contentstore/api/urls.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
""" Course API URLs. """
from __future__ import absolute_import

from django.conf import settings
from django.conf.urls import url

from cms.djangoapps.contentstore.api.views import course_import, course_quality, course_validation


app_name = 'contentstore'

urlpatterns = [
Expand Down
9 changes: 4 additions & 5 deletions cms/djangoapps/contentstore/api/views/course_import.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
"""
APIs related to Course Import.
"""
from __future__ import absolute_import

import base64
import logging
import os

from path import Path as path
from six import text_type

from django.conf import settings

from django.core.files import File
from path import Path as path
from rest_framework import status
from rest_framework.exceptions import AuthenticationFailed
from rest_framework.generics import GenericAPIView
from rest_framework.response import Response
from six import text_type
from user_tasks.models import UserTaskStatus

from contentstore.storage import course_import_export_storage
Expand All @@ -23,7 +23,6 @@

from .utils import course_author_access_required


log = logging.getLogger(__name__)


Expand Down
Loading

0 comments on commit 4c01e20

Please sign in to comment.