Merge pull request #8467 from edx/release-2015-06-10-conflict

Release 2015 06 10 conflict
This commit is contained in:
Edward Zarecor
2015-06-11 10:16:48 -04:00
16 changed files with 382 additions and 45 deletions

View File

@@ -2,6 +2,7 @@
Tests for user enrollment.
"""
import json
import itertools
import unittest
import datetime
@@ -91,11 +92,11 @@ class EnrollmentTestMixin(object):
return response
def assert_enrollment_activation(self, expected_activation, expected_mode=CourseMode.VERIFIED):
def assert_enrollment_activation(self, expected_activation, expected_mode):
"""Change an enrollment's activation and verify its activation and mode are as expected."""
self.assert_enrollment_status(
as_server=True,
mode=None,
mode=expected_mode,
is_active=expected_activation,
expected_status=status.HTTP_200_OK
)
@@ -637,6 +638,58 @@ class EnrollmentTest(EnrollmentTestMixin, ModuleStoreTestCase, APITestCase):
self.assertTrue(is_active)
self.assertEqual(course_mode, CourseMode.HONOR)
@ddt.data(*itertools.product(
(CourseMode.HONOR, CourseMode.VERIFIED),
(CourseMode.HONOR, CourseMode.VERIFIED),
(True, False),
(True, False),
))
@ddt.unpack
def test_change_mode_from_server(self, old_mode, new_mode, old_is_active, new_is_active):
"""
Server-to-server calls should be allowed to change the mode of any
enrollment, as long as the enrollment is not being deactivated during
the same call (this is assumed to be an error on the client's side).
"""
for mode in [CourseMode.HONOR, CourseMode.VERIFIED]:
CourseModeFactory.create(
course_id=self.course.id,
mode_slug=mode,
mode_display_name=mode,
)
# Set up the initial enrollment
self.assert_enrollment_status(as_server=True, mode=old_mode, is_active=old_is_active)
course_mode, is_active = CourseEnrollment.enrollment_mode_for_user(self.user, self.course.id)
self.assertEqual(is_active, old_is_active)
self.assertEqual(course_mode, old_mode)
expected_status = status.HTTP_400_BAD_REQUEST if (
old_mode != new_mode and
old_is_active != new_is_active and
not new_is_active
) else status.HTTP_200_OK
# simulate the server-server api call under test
response = self.assert_enrollment_status(
as_server=True,
mode=new_mode,
is_active=new_is_active,
expected_status=expected_status,
)
course_mode, is_active = CourseEnrollment.enrollment_mode_for_user(self.user, self.course.id)
if expected_status == status.HTTP_400_BAD_REQUEST:
# nothing should have changed
self.assertEqual(is_active, old_is_active)
self.assertEqual(course_mode, old_mode)
# error message should contain specific text. Otto checks for this text in the message.
self.assertRegexpMatches(json.loads(response.content)['message'], 'Enrollment mode mismatch')
else:
# call should have succeeded
self.assertEqual(is_active, new_is_active)
self.assertEqual(course_mode, new_mode)
def test_change_mode_invalid_user(self):
"""
Attempts to change an enrollment for a non-existent user should result in an HTTP 404 for non-server users,

View File

@@ -3,6 +3,8 @@ The Enrollment API Views should be simple, lean HTTP endpoints for API access. T
consist primarily of authentication, request validation, and serialization.
"""
import logging
from ipware.ip import get_ip
from django.core.exceptions import ObjectDoesNotExist
from django.utils.decorators import method_decorator
@@ -31,6 +33,9 @@ from enrollment.errors import (
from student.models import User
log = logging.getLogger(__name__)
class EnrollmentCrossDomainSessionAuth(SessionAuthenticationAllowInactiveUser, SessionAuthenticationCrossDomainCsrf):
"""Session authentication that allows inactive users and cross-domain requests. """
pass
@@ -429,7 +434,18 @@ class EnrollmentListView(APIView, ApiKeyPermissionMixIn):
)
enrollment = api.get_enrollment(username, unicode(course_id))
if has_api_key_permissions and enrollment and enrollment['mode'] != mode:
mode_changed = enrollment and mode is not None and enrollment['mode'] != mode
active_changed = enrollment and is_active is not None and enrollment['is_active'] != is_active
if has_api_key_permissions and (mode_changed or active_changed):
if mode_changed and active_changed and not is_active:
# if the requester wanted to deactivate but specified the wrong mode, fail
# the request (on the assumption that the requester had outdated information
# about the currently active enrollment).
msg = u"Enrollment mode mismatch: active mode={}, requested mode={}. Won't deactivate.".format(
enrollment["mode"], mode
)
log.warning(msg)
return Response(status=status.HTTP_400_BAD_REQUEST, data={"message": msg})
response = api.update_enrollment(username, unicode(course_id), mode=mode, is_active=is_active)
else:
# Will reactivate inactive enrollments.

View File

@@ -522,12 +522,9 @@ function (VideoPlayer, i18n) {
this.youtubeXhr
.always(function (json, status) {
var err = $.isPlainObject(json.error) ||
(
status !== 'success' &&
status !== 'notmodified'
);
if (err) {
// It will work for both if statusCode is 200 or 410.
var didSucceed = (json.error && json.error.code === 410) || status === 'success' || status === 'notmodified';
if (!didSucceed) {
console.log(
'[Video info]: YouTube returned an error for ' +
'video with id "' + id + '".'

View File

@@ -94,7 +94,32 @@ def save_subs_to_store(subs, subs_id, item, language='en'):
return save_to_store(filedata, filename, 'application/json', item.location)
def get_transcripts_from_youtube(youtube_id, settings, i18n):
def youtube_video_transcript_name(youtube_text_api):
"""
Get the transcript name from available transcripts of video
with respect to language from youtube server
"""
# pylint: disable=no-member
utf8_parser = etree.XMLParser(encoding='utf-8')
transcripts_param = {'type': 'list', 'v': youtube_text_api['params']['v']}
lang = youtube_text_api['params']['lang']
# get list of transcripts of specific video
# url-form
# http://video.google.com/timedtext?type=list&v={VideoId}
youtube_response = requests.get('http://' + youtube_text_api['url'], params=transcripts_param)
if youtube_response.status_code == 200 and youtube_response.text:
# pylint: disable=no-member
youtube_data = etree.fromstring(youtube_response.content, parser=utf8_parser)
# iterate all transcripts information from youtube server
for element in youtube_data:
# search specific language code such as 'en' in transcripts info list
if element.tag == 'track' and element.get('lang_code', '') == lang:
return element.get('name')
return None
def get_transcripts_from_youtube(youtube_id, settings, i18n, youtube_transcript_name=''):
"""
Gets transcripts from youtube for youtube_id.
@@ -109,6 +134,12 @@ def get_transcripts_from_youtube(youtube_id, settings, i18n):
youtube_text_api = copy.deepcopy(settings.YOUTUBE['TEXT_API'])
youtube_text_api['params']['v'] = youtube_id
# if the transcript name is not empty on youtube server we have to pass
# name param in url in order to get transcript
# example http://video.google.com/timedtext?lang=en&v={VideoId}&name={transcript_name}
youtube_transcript_name = youtube_video_transcript_name(youtube_text_api)
if youtube_transcript_name:
youtube_text_api['params']['name'] = youtube_transcript_name
data = requests.get('http://' + youtube_text_api['url'], params=youtube_text_api['params'])
if data.status_code != 200 or not data.text: