Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 21 additions & 16 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,27 @@ in the set contentstore.views.item.DETACHED_CATEGORIES nor 'course'.
Studio: Bug fix for text loss in Course Updates when the text exists
before the first tag.

Common: expect_json decorator now puts the parsed json payload into a json attr
on the request instead of overwriting the POST attr

---------- split mongo backend refactoring changelog section ------------

Studio: course catalog, assets, checklists, course outline pages now use course
id syntax w/ restful api style

Common:
separate the non-sql db connection configuration from the modulestore (xblock modeling) configuration.
in split, separate the the db connection and atomic crud ops into a distinct module & class from modulestore

Common: location mapper: % encode periods and dollar signs when used as key in the mapping dict

Common: location mapper: added a bunch of new helper functions for generating
old location style info from a CourseLocator

Common: locators: allow - ~ and . in course, branch, and block ids.

---------- end split mongo backend section ---------

Blades: Hovering over CC button in video player, when transcripts are hidden,
will cause them to show up. Moving the mouse from the CC button will auto hide
them. You can hover over the CC button and then move the mouse to the
Expand Down Expand Up @@ -388,22 +409,6 @@ Studio: Add feedback to end user if there is a problem exporting a course

Studio: Improve link re-writing on imports into a different course-id

---------- split mongo backend refactoring changelog section ------------

Studio: course catalog and course outline pages new use course id syntax w/ restful api style

Common:
separate the non-sql db connection configuration from the modulestore (xblock modeling) configuration.
in split, separate the the db connection and atomic crud ops into a distinct module & class from modulestore

Common: location mapper: % encode periods and dollar signs when used as key in the mapping dict

Common: location mapper: added a bunch of new helper functions for generating old location style info from a CourseLocator

Common: locators: allow - ~ and . in course, branch, and block ids.

---------- end split mongo backend section ---------

XQueue: Fixed (hopefully) worker crash when the connection to RabbitMQ is
dropped suddenly.

Expand Down
2 changes: 1 addition & 1 deletion cms/djangoapps/contentstore/tests/test_checklists.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ def test_update_checklists_index(self):
self.assertEqual('CourseOutline', get_first_item(payload).get('action_url'))
get_first_item(payload)['is_checked'] = True

returned_checklist = json.loads(self.client.post(update_url, json.dumps(payload), "application/json").content)
returned_checklist = json.loads(self.client.ajax_post(update_url, payload).content)
self.assertTrue(get_first_item(returned_checklist).get('is_checked'))
persisted_checklist = self.get_persisted_checklists()[1]
# Verify that persisted checklist does not have expanded action URLs.
Expand Down
38 changes: 18 additions & 20 deletions cms/djangoapps/contentstore/tests/test_contentstore.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

from textwrap import dedent

from django.test.client import Client
from django.test.utils import override_settings
from django.conf import settings
from django.core.urlresolvers import reverse
Expand All @@ -20,7 +19,7 @@
from django.contrib.auth.models import User
from django.dispatch import Signal
from contentstore.utils import get_modulestore
from contentstore.tests.utils import parse_json
from contentstore.tests.utils import parse_json, AjaxEnabledTestClient

from auth.authz import add_user_to_creator_group

Expand Down Expand Up @@ -98,7 +97,7 @@ def setUp(self):
# Save the data that we've just changed to the db.
self.user.save()

self.client = Client()
self.client = AjaxEnabledTestClient()
self.client.login(username=uname, password=password)

def tearDown(self):
Expand Down Expand Up @@ -420,7 +419,7 @@ def test_static_tab_reordering(self):
if tab['type'] == 'static_tab':
reverse_tabs.insert(0, 'i4x://edX/999/static_tab/{0}'.format(tab['url_slug']))

self.client.post(reverse('reorder_static_tabs'), json.dumps({'tabs': reverse_tabs}), "application/json")
self.client.ajax_post(reverse('reorder_static_tabs'), {'tabs': reverse_tabs})

course = module_store.get_item(Location(['i4x', 'edX', '999', 'course', 'Robot_Super_Course', None]))

Expand Down Expand Up @@ -755,7 +754,7 @@ def test_clone_course(self):
expected_children = []
for child_loc_url in source_item.children:
child_loc = Location(child_loc_url)
child_loc = child_loc._replace(
child_loc = child_loc.replace(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When does one use _replace vs. replace?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For locations always use replace since chris created a method which stifles the pylint error of using an internal fn.

tag=dest_location.tag,
org=dest_location.org,
course=dest_location.course
Expand Down Expand Up @@ -1333,7 +1332,7 @@ def setUp(self):
self.user.is_staff = True
self.user.save()

self.client = Client()
self.client = AjaxEnabledTestClient()
self.client.login(username=uname, password=password)

self.course_data = {
Expand All @@ -1344,8 +1343,7 @@ def setUp(self):
}

def tearDown(self):
mongo = MongoClient()
mongo.drop_database(TEST_DATA_CONTENTSTORE['DOC_STORE_CONFIG']['db'])
MongoClient().drop_database(TEST_DATA_CONTENTSTORE['DOC_STORE_CONFIG']['db'])
_CONTENTSTORE.clear()

def test_create_course(self):
Expand Down Expand Up @@ -1394,7 +1392,7 @@ def test_forum_unseeding_with_multiple_courses(self):

def test_create_course_duplicate_course(self):
"""Test new course creation - error path"""
self.client.post(reverse('create_new_course'), self.course_data)
self.client.ajax_post(reverse('create_new_course'), self.course_data)
self.assert_course_creation_failed('There is already a course defined with the same organization, course number, and course run. Please change either organization or course number to be unique.')

def assert_course_creation_failed(self, error_message):
Expand All @@ -1403,7 +1401,7 @@ def assert_course_creation_failed(self, error_message):
"""
course_id = _get_course_id(self.course_data)
initially_enrolled = CourseEnrollment.is_enrolled(self.user, course_id)
resp = self.client.post(reverse('create_new_course'), self.course_data)
resp = self.client.ajax_post(reverse('create_new_course'), self.course_data)
self.assertEqual(resp.status_code, 200)
data = parse_json(resp)
self.assertEqual(data['ErrMsg'], error_message)
Expand All @@ -1413,7 +1411,7 @@ def assert_course_creation_failed(self, error_message):

def test_create_course_duplicate_number(self):
"""Test new course creation - error path"""
self.client.post(reverse('create_new_course'), self.course_data)
self.client.ajax_post(reverse('create_new_course'), self.course_data)
self.course_data['display_name'] = 'Robot Super Course Two'
self.course_data['run'] = '2013_Summer'

Expand All @@ -1422,13 +1420,13 @@ def test_create_course_duplicate_number(self):
def test_create_course_case_change(self):
"""Test new course creation - error path due to case insensitive name equality"""
self.course_data['number'] = 'capital'
self.client.post(reverse('create_new_course'), self.course_data)
self.client.ajax_post(reverse('create_new_course'), self.course_data)
cache_current = self.course_data['org']
self.course_data['org'] = self.course_data['org'].lower()
self.assert_course_creation_failed('There is already a course defined with the same organization and course number. Please change at least one field to be unique.')
self.course_data['org'] = cache_current

self.client.post(reverse('create_new_course'), self.course_data)
self.client.ajax_post(reverse('create_new_course'), self.course_data)
cache_current = self.course_data['number']
self.course_data['number'] = self.course_data['number'].upper()
self.assert_course_creation_failed('There is already a course defined with the same organization and course number. Please change at least one field to be unique.')
Expand All @@ -1437,14 +1435,14 @@ def test_course_substring(self):
"""
Test that a new course can be created whose name is a substring of an existing course
"""
self.client.post(reverse('create_new_course'), self.course_data)
self.client.ajax_post(reverse('create_new_course'), self.course_data)
cache_current = self.course_data['number']
self.course_data['number'] = '{}a'.format(self.course_data['number'])
resp = self.client.post(reverse('create_new_course'), self.course_data)
resp = self.client.ajax_post(reverse('create_new_course'), self.course_data)
self.assertEqual(resp.status_code, 200)
self.course_data['number'] = cache_current
self.course_data['org'] = 'a{}'.format(self.course_data['org'])
resp = self.client.post(reverse('create_new_course'), self.course_data)
resp = self.client.ajax_post(reverse('create_new_course'), self.course_data)
self.assertEqual(resp.status_code, 200)

def test_create_course_with_bad_organization(self):
Expand Down Expand Up @@ -1487,7 +1485,7 @@ def assert_course_permission_denied(self):
"""
Checks that the course did not get created due to a PermissionError.
"""
resp = self.client.post(reverse('create_new_course'), self.course_data)
resp = self.client.ajax_post(reverse('create_new_course'), self.course_data)
self.assertEqual(resp.status_code, 403)

def test_course_index_view_with_no_courses(self):
Expand Down Expand Up @@ -1546,7 +1544,7 @@ def test_create_item(self):
'display_name': 'Section One',
}

resp = self.client.post(reverse('create_item'), section_data)
resp = self.client.ajax_post(reverse('create_item'), section_data)

self.assertEqual(resp.status_code, 200)
data = parse_json(resp)
Expand All @@ -1564,7 +1562,7 @@ def test_capa_module(self):
'category': 'problem'
}

resp = self.client.post(reverse('create_item'), problem_data)
resp = self.client.ajax_post(reverse('create_item'), problem_data)

self.assertEqual(resp.status_code, 200)
payload = parse_json(resp)
Expand Down Expand Up @@ -1934,7 +1932,7 @@ def _create_course(test, course_data):
course_id = _get_course_id(course_data)
new_location = loc_mapper().translate_location(course_id, CourseDescriptor.id_to_location(course_id), False, True)

response = test.client.post(reverse('create_new_course'), course_data)
response = test.client.ajax_post(reverse('create_new_course'), course_data)
test.assertEqual(response.status_code, 200)
data = parse_json(response)
test.assertNotIn('ErrMsg', data)
Expand Down
4 changes: 2 additions & 2 deletions cms/djangoapps/contentstore/tests/test_course_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ def alter_field(self, url, details, field, val):
payload['end_date'] = CourseDetailsViewTest.convert_datetime_to_iso(details.end_date)
payload['enrollment_start'] = CourseDetailsViewTest.convert_datetime_to_iso(details.enrollment_start)
payload['enrollment_end'] = CourseDetailsViewTest.convert_datetime_to_iso(details.enrollment_end)
resp = self.client.post(url, json.dumps(payload), "application/json")
resp = self.client.ajax_post(url, payload)
self.compare_details_with_encoding(json.loads(resp.content), details.__dict__, field + str(val))

@staticmethod
Expand Down Expand Up @@ -462,6 +462,6 @@ def test_post(self):
"short_label": "yo momma",
"weight": 17.3,
}
resp = self.client.post(self.url, grader)
resp = self.client.ajax_post(self.url, grader)
self.assertEqual(resp.status_code, 200)
obj = json.loads(resp.content)
9 changes: 4 additions & 5 deletions cms/djangoapps/contentstore/tests/test_course_updates.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def get_response(content, date):
'course': self.course.location.course,
'provided_id': ''})

resp = self.client.post(url, json.dumps(payload), "application/json")
resp = self.client.ajax_post(url, payload)

return json.loads(resp.content)

Expand Down Expand Up @@ -66,7 +66,6 @@ def get_response(content, date):
payload = json.loads(resp.content)
self.assertTrue(len(payload) == 2)

# can't test non-json paylod b/c expect_json throws error
# try json w/o required fields
self.assertContains(self.client.post(url, json.dumps({'garbage': 1}),
"application/json"),
Expand All @@ -86,7 +85,7 @@ def get_response(content, date):
payload = {'content': content,
'date': 'January 21, 2013'}
self.assertContains(
self.client.post(url, json.dumps(payload), "application/json"),
self.client.ajax_post(url, payload),
'Failed to save', status_code=400)

# update w/ malformed html
Expand All @@ -98,7 +97,7 @@ def get_response(content, date):
'provided_id': ''})

self.assertContains(
self.client.post(url, json.dumps(payload), "application/json"),
self.client.ajax_post(url, payload),
'<garbage')

# set to valid html which would break an xml parser
Expand Down Expand Up @@ -152,7 +151,7 @@ def test_no_ol_course_update(self):
'course': self.course.location.course,
'provided_id': ''})

resp = self.client.post(url, json.dumps(payload), "application/json")
resp = self.client.ajax_post(url, payload)

payload = json.loads(resp.content)

Expand Down
8 changes: 4 additions & 4 deletions cms/djangoapps/contentstore/tests/test_transcripts.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def setUp(self):
'category': 'video',
'type': 'video'
}
resp = self.client.post(reverse('create_item'), data)
resp = self.client.ajax_post(reverse('create_item'), data)
self.item_location = json.loads(resp.content).get('id')
self.assertEqual(resp.status_code, 200)

Expand Down Expand Up @@ -200,7 +200,7 @@ def test_fail_for_non_video_module(self):
'category': 'non_video',
'type': 'non_video'
}
resp = self.client.post(reverse('create_item'), data)
resp = self.client.ajax_post(reverse('create_item'), data)
item_location = json.loads(resp.content).get('id')
data = '<non_video youtube="0.75:JMD_ifUUfsU,1.0:hI10vDNYz4M" />'
modulestore().update_item(item_location, data)
Expand Down Expand Up @@ -411,7 +411,7 @@ def test_fail_for_non_video_module(self):
'category': 'videoalpha',
'type': 'videoalpha'
}
resp = self.client.post(reverse('create_item'), data)
resp = self.client.ajax_post(reverse('create_item'), data)
item_location = json.loads(resp.content).get('id')
subs_id = str(uuid4())
data = textwrap.dedent("""
Expand Down Expand Up @@ -661,7 +661,7 @@ def test_fail_for_non_video_module(self):
'category': 'not_video',
'type': 'not_video'
}
resp = self.client.post(reverse('create_item'), data)
resp = self.client.ajax_post(reverse('create_item'), data)
item_location = json.loads(resp.content).get('id')
subs_id = str(uuid4())
data = textwrap.dedent("""
Expand Down
10 changes: 9 additions & 1 deletion cms/djangoapps/contentstore/tests/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,14 @@ def registration(email):
return Registration.objects.get(user__email=email)


class AjaxEnabledTestClient(Client):
def ajax_post(self, path, data=None, content_type="application/json", **kwargs):
if not isinstance(data, basestring):
data = json.dumps(data or {})
kwargs.setdefault("HTTP_X_REQUESTED_WITH", "XMLHttpRequest")
return self.post(path=path, data=data, content_type=content_type, **kwargs)


@override_settings(MODULESTORE=TEST_MODULESTORE)
class CourseTestCase(ModuleStoreTestCase):
def setUp(self):
Expand All @@ -53,7 +61,7 @@ def setUp(self):
self.user.is_staff = True
self.user.save()

self.client = Client()
self.client = AjaxEnabledTestClient()
self.client.login(username=uname, password=password)

self.course = CourseFactory.create(
Expand Down
10 changes: 5 additions & 5 deletions cms/djangoapps/contentstore/views/component.py
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ def assignment_type_update(request, org, course, category, name):
rsp = CourseGradingModel.get_section_grader_type(location)
elif request.method in ('POST', 'PUT'): # post or put, doesn't matter.
rsp = CourseGradingModel.update_section_grader_type(
location, request.POST
location, request.json
)
return JsonResponse(rsp)

Expand All @@ -332,7 +332,7 @@ def assignment_type_update(request, org, course, category, name):
@expect_json
def create_draft(request):
"Create a draft"
location = request.POST['id']
location = request.json['id']

# check permissions for this user within this course
if not has_access(request.user, location):
Expand All @@ -351,7 +351,7 @@ def publish_draft(request):
"""
Publish a draft
"""
location = request.POST['id']
location = request.json['id']

# check permissions for this user within this course
if not has_access(request.user, location):
Expand All @@ -370,7 +370,7 @@ def publish_draft(request):
@expect_json
def unpublish_unit(request):
"Unpublish a unit"
location = request.POST['id']
location = request.json['id']

# check permissions for this user within this course
if not has_access(request.user, location):
Expand Down Expand Up @@ -413,6 +413,6 @@ def module_info(request, module_location):
elif request.method in ("POST", "PUT"):
rsp = set_module_info(
get_modulestore(location),
location, request.POST
location, request.json
)
return JsonResponse(rsp)
Loading