Commit 484c529d by Calen Pennington

Merge remote-tracking branch 'edx/opaque-keys' into opaque-keys-merge-master

parents 37b1a2f7 741cdf95
...@@ -12,7 +12,6 @@ from xmodule.html_module import HtmlDescriptor ...@@ -12,7 +12,6 @@ from xmodule.html_module import HtmlDescriptor
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
@unittest.skip("Not fixing split until we land opaque-keys 0.9")
class TemplateTests(unittest.TestCase): class TemplateTests(unittest.TestCase):
""" """
Test finding and using the templates (boilerplates) for xblocks. Test finding and using the templates (boilerplates) for xblocks.
...@@ -55,25 +54,25 @@ class TemplateTests(unittest.TestCase): ...@@ -55,25 +54,25 @@ class TemplateTests(unittest.TestCase):
def test_factories(self): def test_factories(self):
test_course = persistent_factories.PersistentCourseFactory.create( test_course = persistent_factories.PersistentCourseFactory.create(
course_id='testx.tempcourse', org='testx', offering='tempcourse', org='testx',
display_name='fun test course', user_id='testbot' display_name='fun test course', user_id='testbot'
) )
self.assertIsInstance(test_course, CourseDescriptor) self.assertIsInstance(test_course, CourseDescriptor)
self.assertEqual(test_course.display_name, 'fun test course') self.assertEqual(test_course.display_name, 'fun test course')
index_info = modulestore('split').get_course_index_info(test_course.location) index_info = modulestore('split').get_course_index_info(test_course.id)
self.assertEqual(index_info['org'], 'testx') self.assertEqual(index_info['org'], 'testx')
self.assertEqual(index_info['_id'], 'testx.tempcourse') self.assertEqual(index_info['offering'], 'tempcourse')
test_chapter = persistent_factories.ItemFactory.create(display_name='chapter 1', test_chapter = persistent_factories.ItemFactory.create(display_name='chapter 1',
parent_location=test_course.location) parent_location=test_course.location)
self.assertIsInstance(test_chapter, SequenceDescriptor) self.assertIsInstance(test_chapter, SequenceDescriptor)
# refetch parent which should now point to child # refetch parent which should now point to child
test_course = modulestore('split').get_course(test_course.id) test_course = modulestore('split').get_course(test_course.id.version_agnostic())
self.assertIn(test_chapter.location.block_id, test_course.children) self.assertIn(test_chapter.location.block_id, test_course.children)
with self.assertRaises(DuplicateCourseError): with self.assertRaises(DuplicateCourseError):
persistent_factories.PersistentCourseFactory.create( persistent_factories.PersistentCourseFactory.create(
course_id='testx.tempcourse', org='testx', offering='tempcourse', org='testx',
display_name='fun test course', user_id='testbot' display_name='fun test course', user_id='testbot'
) )
...@@ -82,7 +81,7 @@ class TemplateTests(unittest.TestCase): ...@@ -82,7 +81,7 @@ class TemplateTests(unittest.TestCase):
Test create_xblock to create non persisted xblocks Test create_xblock to create non persisted xblocks
""" """
test_course = persistent_factories.PersistentCourseFactory.create( test_course = persistent_factories.PersistentCourseFactory.create(
course_id='testx.tempcourse', org='testx', offering='tempcourse', org='testx',
display_name='fun test course', user_id='testbot' display_name='fun test course', user_id='testbot'
) )
...@@ -109,7 +108,7 @@ class TemplateTests(unittest.TestCase): ...@@ -109,7 +108,7 @@ class TemplateTests(unittest.TestCase):
try saving temporary xblocks try saving temporary xblocks
""" """
test_course = persistent_factories.PersistentCourseFactory.create( test_course = persistent_factories.PersistentCourseFactory.create(
course_id='testx.tempcourse', org='testx', offering='tempcourse', org='testx',
display_name='fun test course', user_id='testbot' display_name='fun test course', user_id='testbot'
) )
test_chapter = modulestore('split').create_xblock( test_chapter = modulestore('split').create_xblock(
...@@ -148,7 +147,7 @@ class TemplateTests(unittest.TestCase): ...@@ -148,7 +147,7 @@ class TemplateTests(unittest.TestCase):
def test_delete_course(self): def test_delete_course(self):
test_course = persistent_factories.PersistentCourseFactory.create( test_course = persistent_factories.PersistentCourseFactory.create(
course_id='edu.harvard.history.doomed', org='testx', offering='history.doomed', org='edu.harvard',
display_name='doomed test course', display_name='doomed test course',
user_id='testbot') user_id='testbot')
persistent_factories.ItemFactory.create(display_name='chapter 1', persistent_factories.ItemFactory.create(display_name='chapter 1',
...@@ -171,7 +170,7 @@ class TemplateTests(unittest.TestCase): ...@@ -171,7 +170,7 @@ class TemplateTests(unittest.TestCase):
Test get_block_generations Test get_block_generations
""" """
test_course = persistent_factories.PersistentCourseFactory.create( test_course = persistent_factories.PersistentCourseFactory.create(
course_id='edu.harvard.history.hist101', org='testx', offering='history.hist101', org='edu.harvard',
display_name='history test course', display_name='history test course',
user_id='testbot' user_id='testbot'
) )
...@@ -193,7 +192,9 @@ class TemplateTests(unittest.TestCase): ...@@ -193,7 +192,9 @@ class TemplateTests(unittest.TestCase):
second_problem = persistent_factories.ItemFactory.create( second_problem = persistent_factories.ItemFactory.create(
display_name='problem 2', display_name='problem 2',
parent_location=BlockUsageLocator.make_relative(updated_loc, block_id=sub.location.block_id), parent_location=BlockUsageLocator.make_relative(
updated_loc, block_type='problem', block_id=sub.location.block_id
),
user_id='testbot', category='problem', user_id='testbot', category='problem',
data="<problem></problem>" data="<problem></problem>"
) )
......
...@@ -7,7 +7,7 @@ import lxml ...@@ -7,7 +7,7 @@ import lxml
from contentstore.tests.utils import CourseTestCase from contentstore.tests.utils import CourseTestCase
from contentstore.utils import reverse_course_url from contentstore.utils import reverse_course_url
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
from xmodule.modulestore import parsers from xmodule.modulestore.locator import Locator
class TestCourseIndex(CourseTestCase): class TestCourseIndex(CourseTestCase):
...@@ -38,7 +38,7 @@ class TestCourseIndex(CourseTestCase): ...@@ -38,7 +38,7 @@ class TestCourseIndex(CourseTestCase):
for link in course_link_eles: for link in course_link_eles:
self.assertRegexpMatches( self.assertRegexpMatches(
link.get("href"), link.get("href"),
'course/slashes:{0}'.format(parsers.ALLOWED_ID_CHARS) 'course/slashes:{0}'.format(Locator.ALLOWED_ID_CHARS)
) )
# now test that url # now test that url
outline_response = authed_client.get(link.get("href"), {}, HTTP_ACCEPT='text/html') outline_response = authed_client.get(link.get("href"), {}, HTTP_ACCEPT='text/html')
......
...@@ -118,4 +118,9 @@ def get_cached_content(location): ...@@ -118,4 +118,9 @@ def get_cached_content(location):
def del_cached_content(location): def del_cached_content(location):
cache.delete(unicode(location).encode("utf-8")) # delete content for the given location, as well as for content with run=None.
# it's possible that the content could have been cached without knowing the
# course_key - and so without having the run.
cache.delete_many(
[unicode(loc).encode("utf-8") for loc in [location, location.replace(run=None)]]
)
...@@ -9,6 +9,7 @@ from opaque_keys import InvalidKeyError ...@@ -9,6 +9,7 @@ from opaque_keys import InvalidKeyError
import bson.son import bson.son
import logging import logging
from django.db.models.query_utils import Q from django.db.models.query_utils import Q
from django.db.utils import IntegrityError
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
...@@ -28,69 +29,72 @@ class Migration(DataMigration): ...@@ -28,69 +29,72 @@ class Migration(DataMigration):
loc_map_collection = loc_mapper().location_map loc_map_collection = loc_mapper().location_map
# b/c the Groups table had several entries for each course, we need to ensure we process each unique # b/c the Groups table had several entries for each course, we need to ensure we process each unique
# course only once. The below datastructures help ensure that. # course only once. The below datastructures help ensure that.
hold = {} hold = {} # key of course_id_strings with array of group objects. Should only be org scoped entries
done = set() # or deleted courses
orgs = {} orgs = {} # downcased org to last recorded normal case of the org
query = Q(name__startswith='course_creator_group') query = Q(name='course_creator_group')
for role in ['staff', 'instructor', 'beta_testers', ]: for role in ['staff', 'instructor', 'beta_testers', ]:
query = query | Q(name__startswith=role) query = query | Q(name__startswith=role)
for group in orm['auth.Group'].objects.filter(query).all(): for group in orm['auth.Group'].objects.filter(query).all():
def _migrate_users(correct_course_key, lower_org): def _migrate_users(correct_course_key, role, lower_org):
""" """
Get all the users from the old group and migrate to this course key in the new table Get all the users from the old group and migrate to this course key in the new table
""" """
for user in orm['auth.user'].objects.filter(groups=group).all(): for user in orm['auth.user'].objects.filter(groups=group).all():
entry = orm['student.courseaccessrole']( entry = orm['student.courseaccessrole'](
role=parsed_entry.group('role_id'), user=user, role=role, user=user,
org=correct_course_key.org, course_id=correct_course_key.to_deprecated_string() org=correct_course_key.org, course_id=correct_course_key
) )
entry.save() try:
entry.save()
except IntegrityError:
# already stored
pass
orgs[lower_org] = correct_course_key.org orgs[lower_org] = correct_course_key.org
done.add(correct_course_key)
# should this actually loop through all groups and log any which are not compliant? That is,
# remove the above filter
parsed_entry = self.GROUP_ENTRY_RE.match(group.name) parsed_entry = self.GROUP_ENTRY_RE.match(group.name)
if parsed_entry.group('role_id') == 'course_creator_group': role = parsed_entry.group('role_id')
if role == 'course_creator_group':
for user in orm['auth.user'].objects.filter(groups=group).all(): for user in orm['auth.user'].objects.filter(groups=group).all():
entry = orm['student.courseaccessrole'](role=parsed_entry.group('role_id'), user=user) entry = orm['student.courseaccessrole'](role=role, user=user)
entry.save() entry.save()
else: else:
course_id_string = parsed_entry.group('course_id_string') course_id_string = parsed_entry.group('course_id_string')
try: try:
course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id_string) course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id_string)
if course_key not in done: # course_key is the downcased version, get the normal cased one. loc_mapper() has no
# is the downcased version, get the normal cased one. loc_mapper() has no # methods taking downcased SSCK; so, need to do it manually here
# methods taking downcased SSCK; so, need to do it manually here correct_course_key = self._map_downcased_ssck(course_key, loc_map_collection)
correct_course_key = self._map_downcased_ssck(course_key, loc_map_collection, done) if correct_course_key is not None:
_migrate_users(correct_course_key, course_key.org) _migrate_users(correct_course_key, role, course_key.org)
done.add(course_key)
except InvalidKeyError: except InvalidKeyError:
entry = loc_map_collection.find_one({ entry = loc_map_collection.find_one({
'course_id': re.compile(r'^{}$'.format(course_id_string), re.IGNORECASE) 'course_id': re.compile(r'^{}$'.format(course_id_string), re.IGNORECASE)
}) })
if entry is None: if entry is None:
# not a course_id as far as we can tell hold.setdefault(course_id_string, []).append(group)
if course_id_string not in done:
hold[course_id_string] = group
else: else:
correct_course_key = self._cache_done_return_ssck(entry, done) correct_course_key = SlashSeparatedCourseKey(*entry['_id'].values())
_migrate_users(correct_course_key, entry['lower_id']['org']) _migrate_users(correct_course_key, role, entry['lower_id']['org'])
# see if any in hold ere missed above # see if any in hold were missed above
for not_ssck, group in hold.iteritems(): for held_auth_scope, groups in hold.iteritems():
if not_ssck not in done: # orgs indexed by downcased org
if not_ssck in orgs: held_auth_scope = held_auth_scope.lower()
if held_auth_scope in orgs:
for group in groups:
role = self.GROUP_ENTRY_RE.match(group.name).group('role_id')
# they have org permission # they have org permission
for user in orm['auth.user'].objects.filter(groups=group).all(): for user in orm['auth.user'].objects.filter(groups=group).all():
entry = orm['student.courseaccessrole']( entry = orm['student.courseaccessrole'](
role=parsed_entry.group('role_id'), user=user, role=role,
org=orgs[not_ssck], user=user,
org=orgs[held_auth_scope],
) )
entry.save() entry.save()
else: else:
# should this just log or really make an effort to do the conversion? # don't silently skip unexpected roles
log.warn("Didn't convert role %s", group.name) log.warn("Didn't convert roles %s", [group.name for group in groups])
def backwards(self, orm): def backwards(self, orm):
"Write your backwards methods here." "Write your backwards methods here."
...@@ -98,10 +102,9 @@ class Migration(DataMigration): ...@@ -98,10 +102,9 @@ class Migration(DataMigration):
# the semantic of backwards should be other than perhaps clearing the table. # the semantic of backwards should be other than perhaps clearing the table.
orm['student.courseaccessrole'].objects.all().delete() orm['student.courseaccessrole'].objects.all().delete()
def _map_downcased_ssck(self, downcased_ssck, loc_map_collection, done): def _map_downcased_ssck(self, downcased_ssck, loc_map_collection):
""" """
Get the normal cased version of this downcased slash sep course key and add Get the normal cased version of this downcased slash sep course key
the lowercased locator form to done map
""" """
# given the regex, the son may be an overkill # given the regex, the son may be an overkill
course_son = bson.son.SON([ course_son = bson.son.SON([
...@@ -111,25 +114,11 @@ class Migration(DataMigration): ...@@ -111,25 +114,11 @@ class Migration(DataMigration):
]) ])
entry = loc_map_collection.find_one(course_son) entry = loc_map_collection.find_one(course_son)
if entry: if entry:
return self._cache_done_return_ssck(entry, done) idpart = entry['_id']
return SlashSeparatedCourseKey(idpart['org'], idpart['course'], idpart['name'])
else: else:
return None return None
def _cache_done_return_ssck(self, entry, done):
"""
Add all the various formats which auth may use to the done set and return the ssck for the entry
"""
# cache that the dotted form is done too
if 'lower_course_id' in entry:
done.add(entry['lower_course_id'])
elif 'course_id' in entry:
done.add(entry['course_id'].lower())
elif 'lower_org' in entry:
done.add('{}.{}'.format(entry['lower_org'], entry['lower_offering']))
else:
done.add('{}.{}'.format(entry['org'].lower(), entry['offering'].lower()))
return SlashSeparatedCourseKey(*entry['_id'].values())
models = { models = {
'auth.group': { 'auth.group': {
......
...@@ -79,5 +79,8 @@ setup( ...@@ -79,5 +79,8 @@ setup(
'asset-location = xmodule.modulestore.locations:AssetLocation', 'asset-location = xmodule.modulestore.locations:AssetLocation',
'edx = xmodule.modulestore.locator:BlockUsageLocator', 'edx = xmodule.modulestore.locator:BlockUsageLocator',
], ],
'definition_key': [
'defx = xmodule.modulestore.locator:DefinitionLocator',
],
}, },
) )
...@@ -58,7 +58,7 @@ class DefinitionKey(OpaqueKey): ...@@ -58,7 +58,7 @@ class DefinitionKey(OpaqueKey):
KEY_TYPE = 'definition_key' KEY_TYPE = 'definition_key'
__slots__ = () __slots__ = ()
@abstractmethod @abstractproperty
def block_type(self): def block_type(self):
""" """
The XBlock type of this definition. The XBlock type of this definition.
...@@ -125,6 +125,10 @@ class UsageKey(CourseObjectMixin, OpaqueKey): ...@@ -125,6 +125,10 @@ class UsageKey(CourseObjectMixin, OpaqueKey):
""" """
raise NotImplementedError() raise NotImplementedError()
@property
def block_type(self):
return self.category
class OpaqueKeyReader(IdReader): class OpaqueKeyReader(IdReader):
""" """
......
...@@ -150,6 +150,7 @@ class LocMapperStore(object): ...@@ -150,6 +150,7 @@ class LocMapperStore(object):
entry = self._migrate_if_necessary([entry])[0] entry = self._migrate_if_necessary([entry])[0]
block_id = entry['block_map'].get(self.encode_key_for_mongo(location.name)) block_id = entry['block_map'].get(self.encode_key_for_mongo(location.name))
category = location.category
if block_id is None: if block_id is None:
if add_entry_if_missing: if add_entry_if_missing:
block_id = self._add_to_block_map( block_id = self._add_to_block_map(
...@@ -159,14 +160,15 @@ class LocMapperStore(object): ...@@ -159,14 +160,15 @@ class LocMapperStore(object):
raise ItemNotFoundError(location) raise ItemNotFoundError(location)
else: else:
# jump_to_id uses a None category. # jump_to_id uses a None category.
if location.category is None: if category is None:
if len(block_id) == 1: if len(block_id) == 1:
# unique match (most common case) # unique match (most common case)
category = block_id.keys()[0]
block_id = block_id.values()[0] block_id = block_id.values()[0]
else: else:
raise InvalidLocationError() raise InvalidLocationError()
elif location.category in block_id: elif category in block_id:
block_id = block_id[location.category] block_id = block_id[category]
elif add_entry_if_missing: elif add_entry_if_missing:
block_id = self._add_to_block_map(location, course_son, entry['block_map']) block_id = self._add_to_block_map(location, course_son, entry['block_map'])
else: else:
...@@ -179,10 +181,12 @@ class LocMapperStore(object): ...@@ -179,10 +181,12 @@ class LocMapperStore(object):
) )
published_usage = BlockUsageLocator( published_usage = BlockUsageLocator(
prod_course_locator, prod_course_locator,
block_type=category,
block_id=block_id block_id=block_id
) )
draft_usage = BlockUsageLocator( draft_usage = BlockUsageLocator(
prod_course_locator.for_branch(entry['draft_branch']), prod_course_locator.for_branch(entry['draft_branch']),
block_type=category,
block_id=block_id block_id=block_id
) )
if published: if published:
...@@ -285,6 +289,7 @@ class LocMapperStore(object): ...@@ -285,6 +289,7 @@ class LocMapperStore(object):
org=entry[entry_org], offering=entry[entry_offering], org=entry[entry_org], offering=entry[entry_offering],
branch=entry['prod_branch'] branch=entry['prod_branch']
), ),
block_type=category,
block_id=block_id block_id=block_id
) )
draft_locator = BlockUsageLocator( draft_locator = BlockUsageLocator(
...@@ -292,6 +297,7 @@ class LocMapperStore(object): ...@@ -292,6 +297,7 @@ class LocMapperStore(object):
org=entry[entry_org], offering=entry[entry_offering], org=entry[entry_org], offering=entry[entry_offering],
branch=entry['draft_branch'] branch=entry['draft_branch']
), ),
block_type=category,
block_id=block_id block_id=block_id
) )
self._cache_location_map_entry(location, published_locator, draft_locator) self._cache_location_map_entry(location, published_locator, draft_locator)
......
import re
# Prefix for the branch portion of a locator URL
BRANCH_PREFIX = r"branch/"
# Prefix for the block portion of a locator URL
BLOCK_PREFIX = r"block/"
# Prefix for the version portion of a locator URL, when it is preceded by a course ID
VERSION_PREFIX = r"version/"
ALLOWED_ID_CHARS = r'[\w\-~.:+]'
ALLOWED_ID_RE = re.compile(r'^{}+$'.format(ALLOWED_ID_CHARS), re.UNICODE)
# NOTE: if we need to support period in place of +, make it aggressive (take the first period in the string)
URL_RE_SOURCE = r"""
((?P<org>{ALLOWED_ID_CHARS}+)\+(?P<offering>{ALLOWED_ID_CHARS}+)/?)?
({BRANCH_PREFIX}(?P<branch>{ALLOWED_ID_CHARS}+)/?)?
({VERSION_PREFIX}(?P<version_guid>[A-F0-9]+)/?)?
({BLOCK_PREFIX}(?P<block_id>{ALLOWED_ID_CHARS}+))?
""".format(
ALLOWED_ID_CHARS=ALLOWED_ID_CHARS, BRANCH_PREFIX=BRANCH_PREFIX,
VERSION_PREFIX=VERSION_PREFIX, BLOCK_PREFIX=BLOCK_PREFIX
)
URL_RE = re.compile('^' + URL_RE_SOURCE + '$', re.IGNORECASE | re.VERBOSE | re.UNICODE)
def parse_url(string):
"""
followed by either a version_guid or a org + offering pair. If tag_optional, then
the url does not have to start with the tag and edx will be assumed.
Examples:
'edx:version/0123FFFF'
'edx:mit.eecs.6002x'
'edx:mit.eecs.6002x/branch/published'
'edx:mit.eecs.6002x/branch/published/block/HW3'
'edx:mit.eecs.6002x/branch/published/version/000eee12345/block/HW3'
This returns None if string cannot be parsed.
If it can be parsed as a version_guid with no preceding org + offering, returns a dict
with key 'version_guid' and the value,
If it can be parsed as a org + offering, returns a dict
with key 'id' and optional keys 'branch' and 'version_guid'.
"""
match = URL_RE.match(string)
if not match:
return None
matched_dict = match.groupdict()
return matched_dict
def parse_block_ref(string):
r"""
A block_ref is a string of url safe characters (see ALLOWED_ID_CHARS)
If string is a block_ref, returns a dict with key 'block_ref' and the value,
otherwise returns None.
"""
if ALLOWED_ID_RE.match(string):
return {'block_id': string}
return None
...@@ -108,6 +108,7 @@ class CachingDescriptorSystem(MakoDescriptorSystem): ...@@ -108,6 +108,7 @@ class CachingDescriptorSystem(MakoDescriptorSystem):
offering=course_entry_override.get('offering'), offering=course_entry_override.get('offering'),
branch=course_entry_override.get('branch'), branch=course_entry_override.get('branch'),
), ),
block_type=json_data.get('category'),
block_id=block_id, block_id=block_id,
) )
...@@ -131,6 +132,7 @@ class CachingDescriptorSystem(MakoDescriptorSystem): ...@@ -131,6 +132,7 @@ class CachingDescriptorSystem(MakoDescriptorSystem):
self, self,
BlockUsageLocator( BlockUsageLocator(
CourseLocator(version_guid=course_entry_override['structure']['_id']), CourseLocator(version_guid=course_entry_override['structure']['_id']),
block_type='error',
block_id=block_id block_id=block_id
), ),
error_msg=exc_info_to_str(sys.exc_info()) error_msg=exc_info_to_str(sys.exc_info())
......
...@@ -8,14 +8,14 @@ class DefinitionLazyLoader(object): ...@@ -8,14 +8,14 @@ class DefinitionLazyLoader(object):
object doesn't force access during init but waits until client wants the object doesn't force access during init but waits until client wants the
definition. Only works if the modulestore is a split mongo store. definition. Only works if the modulestore is a split mongo store.
""" """
def __init__(self, modulestore, definition_id): def __init__(self, modulestore, block_type, definition_id):
""" """
Simple placeholder for yet-to-be-fetched data Simple placeholder for yet-to-be-fetched data
:param modulestore: the pymongo db connection with the definitions :param modulestore: the pymongo db connection with the definitions
:param definition_locator: the id of the record in the above to fetch :param definition_locator: the id of the record in the above to fetch
""" """
self.modulestore = modulestore self.modulestore = modulestore
self.definition_locator = DefinitionLocator(definition_id) self.definition_locator = DefinitionLocator(block_type, definition_id)
def fetch(self): def fetch(self):
""" """
......
...@@ -152,7 +152,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase): ...@@ -152,7 +152,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
if lazy: if lazy:
for block in new_module_data.itervalues(): for block in new_module_data.itervalues():
block['definition'] = DefinitionLazyLoader(self, block['definition']) block['definition'] = DefinitionLazyLoader(self, block['category'], block['definition'])
else: else:
# Load all descendants by id # Load all descendants by id
descendent_definitions = self.db_connection.find_matching_definitions({ descendent_definitions = self.db_connection.find_matching_definitions({
...@@ -242,11 +242,6 @@ class SplitMongoModuleStore(ModuleStoreWriteBase): ...@@ -242,11 +242,6 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
:param course_locator: any subclass of CourseLocator :param course_locator: any subclass of CourseLocator
''' '''
# NOTE: if and when this uses cache, the update if changed logic will break if the cache
# holds the same objects as the descriptors!
if not course_locator.is_fully_specified():
raise InsufficientSpecificationError('Not fully specified: %s' % course_locator)
if course_locator.org and course_locator.offering and course_locator.branch: if course_locator.org and course_locator.offering and course_locator.branch:
# use the course id # use the course id
index = self.db_connection.get_course_index(course_locator) index = self.db_connection.get_course_index(course_locator)
...@@ -258,6 +253,8 @@ class SplitMongoModuleStore(ModuleStoreWriteBase): ...@@ -258,6 +253,8 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
if course_locator.version_guid is not None and version_guid != course_locator.version_guid: if course_locator.version_guid is not None and version_guid != course_locator.version_guid:
# This may be a bit too touchy but it's hard to infer intent # This may be a bit too touchy but it's hard to infer intent
raise VersionConflictError(course_locator, version_guid) raise VersionConflictError(course_locator, version_guid)
elif course_locator.version_guid is None:
raise InsufficientSpecificationError(course_locator)
else: else:
# TODO should this raise an exception if branch was provided? # TODO should this raise an exception if branch was provided?
version_guid = course_locator.version_guid version_guid = course_locator.version_guid
...@@ -322,9 +319,6 @@ class SplitMongoModuleStore(ModuleStoreWriteBase): ...@@ -322,9 +319,6 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
def get_course(self, course_id, depth=None): def get_course(self, course_id, depth=None):
''' '''
Gets the course descriptor for the course identified by the locator Gets the course descriptor for the course identified by the locator
which may or may not be a blockLocator.
raises InsufficientSpecificationError
''' '''
assert(isinstance(course_id, CourseLocator)) assert(isinstance(course_id, CourseLocator))
course_entry = self._lookup_course(course_id) course_entry = self._lookup_course(course_id)
...@@ -458,6 +452,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase): ...@@ -458,6 +452,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
return [ return [
BlockUsageLocator.make_relative( BlockUsageLocator.make_relative(
locator, locator,
block_type=course['structure']['blocks'][parent_id].get('category'),
block_id=LocMapperStore.decode_key_from_mongo(parent_id), block_id=LocMapperStore.decode_key_from_mongo(parent_id),
) )
for parent_id in items for parent_id in items
...@@ -471,12 +466,13 @@ class SplitMongoModuleStore(ModuleStoreWriteBase): ...@@ -471,12 +466,13 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
course = self._lookup_course(course_key) course = self._lookup_course(course_key)
items = {LocMapperStore.decode_key_from_mongo(block_id) for block_id in course['structure']['blocks'].keys()} items = {LocMapperStore.decode_key_from_mongo(block_id) for block_id in course['structure']['blocks'].keys()}
items.remove(course['structure']['root']) items.remove(course['structure']['root'])
for block_id, block_data in course['structure']['blocks'].iteritems(): blocks = course['structure']['blocks']
for block_id, block_data in blocks.iteritems():
items.difference_update(block_data.get('fields', {}).get('children', [])) items.difference_update(block_data.get('fields', {}).get('children', []))
if block_data['category'] in detached_categories: if block_data['category'] in detached_categories:
items.discard(LocMapperStore.decode_key_from_mongo(block_id)) items.discard(LocMapperStore.decode_key_from_mongo(block_id))
return [ return [
BlockUsageLocator(course_key=course_key, block_id=block_id) BlockUsageLocator(course_key=course_key, block_type=blocks[block_id]['category'], block_id=block_id)
for block_id in items for block_id in items
] ]
...@@ -613,11 +609,11 @@ class SplitMongoModuleStore(ModuleStoreWriteBase): ...@@ -613,11 +609,11 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
# convert the results value sets to locators # convert the results value sets to locators
for k, versions in result.iteritems(): for k, versions in result.iteritems():
result[k] = [ result[k] = [
BlockUsageLocator(CourseLocator(version_guid=version), block_id=block_id) block_locator.for_version(version)
for version in versions for version in versions
] ]
return VersionTree( return VersionTree(
BlockUsageLocator(CourseLocator(version_guid=possible_roots[0]), block_id=block_id), block_locator.for_version(possible_roots[0]),
result result
) )
...@@ -650,7 +646,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase): ...@@ -650,7 +646,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
'schema_version': self.SCHEMA_VERSION, 'schema_version': self.SCHEMA_VERSION,
} }
self.db_connection.insert_definition(document) self.db_connection.insert_definition(document)
definition_locator = DefinitionLocator(new_id) definition_locator = DefinitionLocator(category, new_id)
return definition_locator return definition_locator
def update_definition_from_data(self, definition_locator, new_def_data, user_id): def update_definition_from_data(self, definition_locator, new_def_data, user_id):
...@@ -685,7 +681,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase): ...@@ -685,7 +681,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
old_definition['edit_info']['previous_version'] = definition_locator.definition_id old_definition['edit_info']['previous_version'] = definition_locator.definition_id
old_definition['schema_version'] = self.SCHEMA_VERSION old_definition['schema_version'] = self.SCHEMA_VERSION
self.db_connection.insert_definition(old_definition) self.db_connection.insert_definition(old_definition)
return DefinitionLocator(old_definition['_id']), True return DefinitionLocator(old_definition['category'], old_definition['_id']), True
else: else:
return definition_locator, False return definition_locator, False
...@@ -829,11 +825,13 @@ class SplitMongoModuleStore(ModuleStoreWriteBase): ...@@ -829,11 +825,13 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
self._update_head(index_entry, course_or_parent_locator.branch, new_id) self._update_head(index_entry, course_or_parent_locator.branch, new_id)
item_loc = BlockUsageLocator( item_loc = BlockUsageLocator(
course_or_parent_locator.version_agnostic(), course_or_parent_locator.version_agnostic(),
block_type=category,
block_id=new_block_id, block_id=new_block_id,
) )
else: else:
item_loc = BlockUsageLocator( item_loc = BlockUsageLocator(
CourseLocator(version_guid=new_id), CourseLocator(version_guid=new_id),
block_type=category,
block_id=new_block_id, block_id=new_block_id,
) )
...@@ -1029,7 +1027,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase): ...@@ -1029,7 +1027,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
course_key = CourseLocator(version_guid=new_id) course_key = CourseLocator(version_guid=new_id)
# fetch and return the new item--fetching is unnecessary but a good qc step # fetch and return the new item--fetching is unnecessary but a good qc step
new_locator = BlockUsageLocator(course_key, descriptor.location.block_id) new_locator = descriptor.location.map_into_course(course_key)
return self.get_item(new_locator) return self.get_item(new_locator)
else: else:
# nothing changed, just return the one sent in # nothing changed, just return the one sent in
...@@ -1101,18 +1099,14 @@ class SplitMongoModuleStore(ModuleStoreWriteBase): ...@@ -1101,18 +1099,14 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
self._update_head(index_entry, xblock.location.branch, new_id) self._update_head(index_entry, xblock.location.branch, new_id)
# fetch and return the new item--fetching is unnecessary but a good qc step # fetch and return the new item--fetching is unnecessary but a good qc step
return self.get_item( return self.get_item(xblock.location.for_version(new_id))
BlockUsageLocator(
xblock.location.course_key.for_version(new_id),
block_id=xblock.location.block_id,
)
)
else: else:
return xblock return xblock
def _persist_subdag(self, xblock, user_id, structure_blocks, new_id): def _persist_subdag(self, xblock, user_id, structure_blocks, new_id):
# persist the definition if persisted != passed # persist the definition if persisted != passed
new_def_data = self._filter_special_fields(xblock.get_explicitly_set_fields_by_scope(Scope.content)) new_def_data = self._filter_special_fields(xblock.get_explicitly_set_fields_by_scope(Scope.content))
is_updated = False
if xblock.definition_locator is None or isinstance(xblock.definition_locator.definition_id, LocalId): if xblock.definition_locator is None or isinstance(xblock.definition_locator.definition_id, LocalId):
xblock.definition_locator = self.create_definition_from_data( xblock.definition_locator = self.create_definition_from_data(
new_def_data, xblock.category, user_id) new_def_data, xblock.category, user_id)
...@@ -1134,9 +1128,6 @@ class SplitMongoModuleStore(ModuleStoreWriteBase): ...@@ -1134,9 +1128,6 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
else: else:
is_new = False is_new = False
encoded_block_id = LocMapperStore.encode_key_for_mongo(xblock.location.block_id) encoded_block_id = LocMapperStore.encode_key_for_mongo(xblock.location.block_id)
is_updated = is_updated or (
xblock.has_children and structure_blocks[encoded_block_id]['fields']['children'] != xblock.children
)
children = [] children = []
if xblock.has_children: if xblock.has_children:
...@@ -1147,6 +1138,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase): ...@@ -1147,6 +1138,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
children.append(child_block.location.block_id) children.append(child_block.location.block_id)
else: else:
children.append(child) children.append(child)
is_updated = is_updated or structure_blocks[encoded_block_id]['fields']['children'] != children
block_fields = xblock.get_explicitly_set_fields_by_scope(Scope.settings) block_fields = xblock.get_explicitly_set_fields_by_scope(Scope.settings)
if not is_new and not is_updated: if not is_new and not is_updated:
...@@ -1419,9 +1411,9 @@ class SplitMongoModuleStore(ModuleStoreWriteBase): ...@@ -1419,9 +1411,9 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
if isinstance(definition, DefinitionLazyLoader): if isinstance(definition, DefinitionLazyLoader):
return definition.definition_locator return definition.definition_locator
elif '_id' not in definition: elif '_id' not in definition:
return DefinitionLocator(LocalId()) return DefinitionLocator(definition.get('category'), LocalId())
else: else:
return DefinitionLocator(definition['_id']) return DefinitionLocator(definition['category'], definition['_id'])
def get_modulestore_type(self, course_id): def get_modulestore_type(self, course_id):
""" """
......
...@@ -32,14 +32,14 @@ class PersistentCourseFactory(SplitFactory): ...@@ -32,14 +32,14 @@ class PersistentCourseFactory(SplitFactory):
# pylint: disable=W0613 # pylint: disable=W0613
@classmethod @classmethod
def _create(cls, target_class, course_id='testX.999', org='testX', user_id='test_user', def _create(cls, target_class, offering='999', org='testX', user_id='test_user',
master_branch='draft', **kwargs): master_branch='draft', **kwargs):
modulestore = kwargs.pop('modulestore') modulestore = kwargs.pop('modulestore')
root_block_id = kwargs.pop('root_block_id', 'course') root_block_id = kwargs.pop('root_block_id', 'course')
# Write the data to the mongo datastore # Write the data to the mongo datastore
new_course = modulestore.create_course( new_course = modulestore.create_course(
course_id, org, user_id, fields=kwargs, org, offering, user_id, fields=kwargs,
master_branch=master_branch, root_block_id=root_block_id master_branch=master_branch, root_block_id=root_block_id
) )
......
...@@ -267,6 +267,7 @@ class TestLocationMapper(LocMapperSetupSansDjango): ...@@ -267,6 +267,7 @@ class TestLocationMapper(LocMapperSetupSansDjango):
) )
prob_locator = BlockUsageLocator( prob_locator = BlockUsageLocator(
prob_course_key, prob_course_key,
block_type='problem',
block_id='problem2', block_id='problem2',
) )
prob_location = loc_mapper().translate_locator_to_location(prob_locator) prob_location = loc_mapper().translate_locator_to_location(prob_locator)
...@@ -289,20 +290,21 @@ class TestLocationMapper(LocMapperSetupSansDjango): ...@@ -289,20 +290,21 @@ class TestLocationMapper(LocMapperSetupSansDjango):
prob_location = loc_mapper().translate_locator_to_location(prob_locator, get_course=True) prob_location = loc_mapper().translate_locator_to_location(prob_locator, get_course=True)
self.assertEqual(prob_location, SlashSeparatedCourseKey(org, course, run)) self.assertEqual(prob_location, SlashSeparatedCourseKey(org, course, run))
# explicit branch # explicit branch
prob_locator = BlockUsageLocator( prob_locator = prob_locator.for_branch('draft')
prob_course_key.for_branch('draft'), block_id=prob_locator.block_id
)
prob_location = loc_mapper().translate_locator_to_location(prob_locator) prob_location = loc_mapper().translate_locator_to_location(prob_locator)
# Even though the problem was set as draft, we always return revision=None to work # Even though the problem was set as draft, we always return revision=None to work
# with old mongo/draft modulestores. # with old mongo/draft modulestores.
self.assertEqual(prob_location, Location(org, course, run, 'problem', 'abc123', None)) self.assertEqual(prob_location, Location(org, course, run, 'problem', 'abc123', None))
prob_locator = BlockUsageLocator(prob_course_key.for_branch('production'), block_id='problem2') prob_locator = BlockUsageLocator(
prob_course_key.for_branch('production'),
block_type='problem', block_id='problem2'
)
prob_location = loc_mapper().translate_locator_to_location(prob_locator) prob_location = loc_mapper().translate_locator_to_location(prob_locator)
self.assertEqual(prob_location, Location(org, course, run, 'problem', 'abc123', None)) self.assertEqual(prob_location, Location(org, course, run, 'problem', 'abc123', None))
# same for chapter except chapter cannot be draft in old system # same for chapter except chapter cannot be draft in old system
chap_locator = BlockUsageLocator( chap_locator = BlockUsageLocator(
prob_course_key.for_branch('production'), prob_course_key.for_branch('production'),
block_id='chapter48f', block_type='chapter', block_id='chapter48f',
) )
chap_location = loc_mapper().translate_locator_to_location(chap_locator) chap_location = loc_mapper().translate_locator_to_location(chap_locator)
self.assertEqual(chap_location, Location(org, course, run, 'chapter', '48f23a10395384929234')) self.assertEqual(chap_location, Location(org, course, run, 'chapter', '48f23a10395384929234'))
...@@ -311,7 +313,7 @@ class TestLocationMapper(LocMapperSetupSansDjango): ...@@ -311,7 +313,7 @@ class TestLocationMapper(LocMapperSetupSansDjango):
chap_location = loc_mapper().translate_locator_to_location(chap_locator) chap_location = loc_mapper().translate_locator_to_location(chap_locator)
self.assertEqual(chap_location, Location(org, course, run, 'chapter', '48f23a10395384929234')) self.assertEqual(chap_location, Location(org, course, run, 'chapter', '48f23a10395384929234'))
chap_locator = BlockUsageLocator( chap_locator = BlockUsageLocator(
prob_course_key.for_branch('production'), block_id='chapter48f' prob_course_key.for_branch('production'), block_type='chapter', block_id='chapter48f'
) )
chap_location = loc_mapper().translate_locator_to_location(chap_locator) chap_location = loc_mapper().translate_locator_to_location(chap_locator)
self.assertEqual(chap_location, Location(org, course, run, 'chapter', '48f23a10395384929234')) self.assertEqual(chap_location, Location(org, course, run, 'chapter', '48f23a10395384929234'))
...@@ -319,7 +321,7 @@ class TestLocationMapper(LocMapperSetupSansDjango): ...@@ -319,7 +321,7 @@ class TestLocationMapper(LocMapperSetupSansDjango):
# look for non-existent problem # look for non-existent problem
prob_locator2 = BlockUsageLocator( prob_locator2 = BlockUsageLocator(
prob_course_key.for_branch('draft'), prob_course_key.for_branch('draft'),
block_id='problem3' block_type='problem', block_id='problem3'
) )
prob_location = loc_mapper().translate_locator_to_location(prob_locator2) prob_location = loc_mapper().translate_locator_to_location(prob_locator2)
self.assertIsNone(prob_location, 'Found non-existent problem') self.assertIsNone(prob_location, 'Found non-existent problem')
......
...@@ -105,6 +105,7 @@ class SplitWMongoCourseBoostrapper(unittest.TestCase): ...@@ -105,6 +105,7 @@ class SplitWMongoCourseBoostrapper(unittest.TestCase):
# create pointer for split # create pointer for split
course_or_parent_locator = BlockUsageLocator( course_or_parent_locator = BlockUsageLocator(
course_key=self.split_course_key, course_key=self.split_course_key,
block_type=parent_category,
block_id=parent_name block_id=parent_name
) )
else: else:
......
...@@ -37,7 +37,7 @@ class ControllerQueryService(GradingService): ...@@ -37,7 +37,7 @@ class ControllerQueryService(GradingService):
def check_combined_notifications(self, course_id, student_id, user_is_staff, last_time_viewed): def check_combined_notifications(self, course_id, student_id, user_is_staff, last_time_viewed):
params = { params = {
'student_id': student_id, 'student_id': student_id,
'course_id': course_id, 'course_id': course_id.to_deprecated_string(),
'user_is_staff': user_is_staff, 'user_is_staff': user_is_staff,
'last_time_viewed': last_time_viewed, 'last_time_viewed': last_time_viewed,
} }
......
...@@ -85,7 +85,7 @@ class PeerGradingService(GradingService): ...@@ -85,7 +85,7 @@ class PeerGradingService(GradingService):
return result return result
def get_problem_list(self, course_id, grader_id): def get_problem_list(self, course_id, grader_id):
params = {'course_id': course_id, 'student_id': grader_id} params = {'course_id': course_id.to_deprecated_string(), 'student_id': grader_id}
result = self.get(self.get_problem_list_url, params) result = self.get(self.get_problem_list_url, params)
if 'problem_list' in result: if 'problem_list' in result:
...@@ -100,7 +100,7 @@ class PeerGradingService(GradingService): ...@@ -100,7 +100,7 @@ class PeerGradingService(GradingService):
return result return result
def get_notifications(self, course_id, grader_id): def get_notifications(self, course_id, grader_id):
params = {'course_id': course_id, 'student_id': grader_id} params = {'course_id': course_id.to_deprecated_string(), 'student_id': grader_id}
result = self.get(self.get_notifications_url, params) result = self.get(self.get_notifications_url, params)
self._record_result( self._record_result(
'get_notifications', 'get_notifications',
......
...@@ -182,7 +182,7 @@ def _create_comment(request, course_key, thread_id=None, parent_id=None): ...@@ -182,7 +182,7 @@ def _create_comment(request, course_key, thread_id=None, parent_id=None):
'anonymous': anonymous, 'anonymous': anonymous,
'anonymous_to_peers': anonymous_to_peers, 'anonymous_to_peers': anonymous_to_peers,
'user_id': request.user.id, 'user_id': request.user.id,
'course_id': course_key, 'course_id': course_key.to_deprecated_string(),
'thread_id': thread_id, 'thread_id': thread_id,
'parent_id': parent_id, 'parent_id': parent_id,
}) })
......
...@@ -285,7 +285,7 @@ class UserProfileTestCase(ModuleStoreTestCase): ...@@ -285,7 +285,7 @@ class UserProfileTestCase(ModuleStoreTestCase):
StringEndsWithMatcher('/users/{}/active_threads'.format(self.profiled_user.id)), StringEndsWithMatcher('/users/{}/active_threads'.format(self.profiled_user.id)),
data=None, data=None,
params=PartialDictMatcher({ params=PartialDictMatcher({
"course_id": self.course.id, "course_id": self.course.id.to_deprecated_string(),
"page": params.get("page", 1), "page": params.get("page", 1),
"per_page": views.THREADS_PER_PAGE "per_page": views.THREADS_PER_PAGE
}), }),
......
...@@ -9,7 +9,6 @@ from django.conf import settings ...@@ -9,7 +9,6 @@ from django.conf import settings
from django.http import HttpResponse, Http404 from django.http import HttpResponse, Http404
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from xmodule.course_module import CourseDescriptor
from xmodule.modulestore.locations import SlashSeparatedCourseKey from xmodule.modulestore.locations import SlashSeparatedCourseKey
from xmodule.open_ended_grading_classes.grading_service_module import GradingService, GradingServiceError from xmodule.open_ended_grading_classes.grading_service_module import GradingService, GradingServiceError
from xmodule.modulestore.django import ModuleI18nService from xmodule.modulestore.django import ModuleI18nService
...@@ -116,7 +115,7 @@ class StaffGradingService(GradingService): ...@@ -116,7 +115,7 @@ class StaffGradingService(GradingService):
Raises: Raises:
GradingServiceError: something went wrong with the connection. GradingServiceError: something went wrong with the connection.
""" """
params = {'course_id': course_id, 'grader_id': grader_id} params = {'course_id': course_id.to_deprecated_string(), 'grader_id': grader_id}
result = self.get(self.get_problem_list_url, params) result = self.get(self.get_problem_list_url, params)
tags = [u'course_id:{}'.format(course_id)] tags = [u'course_id:{}'.format(course_id)]
self._record_result('get_problem_list', result, tags) self._record_result('get_problem_list', result, tags)
...@@ -148,7 +147,7 @@ class StaffGradingService(GradingService): ...@@ -148,7 +147,7 @@ class StaffGradingService(GradingService):
self.get( self.get(
self.get_next_url, self.get_next_url,
params={ params={
'location': location, 'location': location.to_deprecated_string(),
'grader_id': grader_id 'grader_id': grader_id
} }
) )
...@@ -170,7 +169,7 @@ class StaffGradingService(GradingService): ...@@ -170,7 +169,7 @@ class StaffGradingService(GradingService):
Raises: Raises:
GradingServiceError if there's a problem connecting. GradingServiceError if there's a problem connecting.
""" """
data = {'course_id': course_id, data = {'course_id': course_id.to_deprecated_string(),
'submission_id': submission_id, 'submission_id': submission_id,
'score': score, 'score': score,
'feedback': feedback, 'feedback': feedback,
...@@ -186,7 +185,7 @@ class StaffGradingService(GradingService): ...@@ -186,7 +185,7 @@ class StaffGradingService(GradingService):
return result return result
def get_notifications(self, course_id): def get_notifications(self, course_id):
params = {'course_id': course_id} params = {'course_id': course_id.to_deprecated_string()}
result = self.get(self.get_notifications_url, params) result = self.get(self.get_notifications_url, params)
tags = [ tags = [
u'course_id:{}'.format(course_id), u'course_id:{}'.format(course_id),
...@@ -274,7 +273,7 @@ def get_next(request, course_id): ...@@ -274,7 +273,7 @@ def get_next(request, course_id):
', '.join(missing))) ', '.join(missing)))
grader_id = unique_id_for_user(request.user) grader_id = unique_id_for_user(request.user)
p = request.POST p = request.POST
location = p['location'] location = course_key.make_usage_key_from_deprecated_string(p['location'])
return HttpResponse(json.dumps(_get_next(course_key, grader_id, location)), return HttpResponse(json.dumps(_get_next(course_key, grader_id, location)),
mimetype="application/json") mimetype="application/json")
...@@ -400,7 +399,7 @@ def save_grade(request, course_id): ...@@ -400,7 +399,7 @@ def save_grade(request, course_id):
grader_id = unique_id_for_user(request.user) grader_id = unique_id_for_user(request.user)
location = p['location'] location = course_key.make_usage_key_from_deprecated_string(p['location'])
try: try:
result = staff_grading_service().save_grade(course_key, result = staff_grading_service().save_grade(course_key,
......
...@@ -109,13 +109,13 @@ class TestStaffGradingService(ModuleStoreTestCase, LoginEnrollmentTestCase): ...@@ -109,13 +109,13 @@ class TestStaffGradingService(ModuleStoreTestCase, LoginEnrollmentTestCase):
self.student = 'view@test.com' self.student = 'view@test.com'
self.instructor = 'view2@test.com' self.instructor = 'view2@test.com'
self.password = 'foo' self.password = 'foo'
self.location = 'TestLocation'
self.create_account('u1', self.student, self.password) self.create_account('u1', self.student, self.password)
self.create_account('u2', self.instructor, self.password) self.create_account('u2', self.instructor, self.password)
self.activate_user(self.student) self.activate_user(self.student)
self.activate_user(self.instructor) self.activate_user(self.instructor)
self.course_id = SlashSeparatedCourseKey("edX", "toy", "2012_Fall") self.course_id = SlashSeparatedCourseKey("edX", "toy", "2012_Fall")
self.location_string = self.course_id.make_usage_key('html', 'TestLocation').to_deprecated_string()
self.toy = modulestore().get_course(self.course_id) self.toy = modulestore().get_course(self.course_id)
make_instructor(self.toy, self.instructor) make_instructor(self.toy, self.instructor)
...@@ -140,7 +140,7 @@ class TestStaffGradingService(ModuleStoreTestCase, LoginEnrollmentTestCase): ...@@ -140,7 +140,7 @@ class TestStaffGradingService(ModuleStoreTestCase, LoginEnrollmentTestCase):
self.login(self.instructor, self.password) self.login(self.instructor, self.password)
url = reverse('staff_grading_get_next', kwargs={'course_id': self.course_id.to_deprecated_string()}) url = reverse('staff_grading_get_next', kwargs={'course_id': self.course_id.to_deprecated_string()})
data = {'location': self.location} data = {'location': self.location_string}
response = check_for_post_code(self, 200, url, data) response = check_for_post_code(self, 200, url, data)
...@@ -165,7 +165,7 @@ class TestStaffGradingService(ModuleStoreTestCase, LoginEnrollmentTestCase): ...@@ -165,7 +165,7 @@ class TestStaffGradingService(ModuleStoreTestCase, LoginEnrollmentTestCase):
data = {'score': '12', data = {'score': '12',
'feedback': 'great!', 'feedback': 'great!',
'submission_id': '123', 'submission_id': '123',
'location': self.location, 'location': self.location_string,
'submission_flagged': "true", 'submission_flagged': "true",
'rubric_scores[]': ['1', '2']} 'rubric_scores[]': ['1', '2']}
if skip: if skip:
...@@ -227,7 +227,7 @@ class TestStaffGradingService(ModuleStoreTestCase, LoginEnrollmentTestCase): ...@@ -227,7 +227,7 @@ class TestStaffGradingService(ModuleStoreTestCase, LoginEnrollmentTestCase):
'score': '12', 'score': '12',
'feedback': '', 'feedback': '',
'submission_id': '123', 'submission_id': '123',
'location': self.location, 'location': self.location_string,
'submission_flagged': "false", 'submission_flagged': "false",
'rubric_scores[]': ['1', '2'] 'rubric_scores[]': ['1', '2']
} }
...@@ -262,13 +262,13 @@ class TestPeerGradingService(ModuleStoreTestCase, LoginEnrollmentTestCase): ...@@ -262,13 +262,13 @@ class TestPeerGradingService(ModuleStoreTestCase, LoginEnrollmentTestCase):
self.student = 'view@test.com' self.student = 'view@test.com'
self.instructor = 'view2@test.com' self.instructor = 'view2@test.com'
self.password = 'foo' self.password = 'foo'
self.location = 'TestLocation'
self.create_account('u1', self.student, self.password) self.create_account('u1', self.student, self.password)
self.create_account('u2', self.instructor, self.password) self.create_account('u2', self.instructor, self.password)
self.activate_user(self.student) self.activate_user(self.student)
self.activate_user(self.instructor) self.activate_user(self.instructor)
self.course_id = SlashSeparatedCourseKey("edX", "toy", "2012_Fall") self.course_id = SlashSeparatedCourseKey("edX", "toy", "2012_Fall")
self.location_string = self.course_id.make_usage_key('html', 'TestLocation').to_deprecated_string()
self.toy = modulestore().get_course(self.course_id) self.toy = modulestore().get_course(self.course_id)
location = "i4x://edX/toy/peergrading/init" location = "i4x://edX/toy/peergrading/init"
field_data = DictFieldData({'data': "<peergrading/>", 'location': location, 'category':'peergrading'}) field_data = DictFieldData({'data': "<peergrading/>", 'location': location, 'category':'peergrading'})
...@@ -292,7 +292,7 @@ class TestPeerGradingService(ModuleStoreTestCase, LoginEnrollmentTestCase): ...@@ -292,7 +292,7 @@ class TestPeerGradingService(ModuleStoreTestCase, LoginEnrollmentTestCase):
self.logout() self.logout()
def test_get_next_submission_success(self): def test_get_next_submission_success(self):
data = {'location': self.location} data = {'location': self.location_string}
response = self.peer_module.get_next_submission(data) response = self.peer_module.get_next_submission(data)
content = response content = response
...@@ -312,7 +312,7 @@ class TestPeerGradingService(ModuleStoreTestCase, LoginEnrollmentTestCase): ...@@ -312,7 +312,7 @@ class TestPeerGradingService(ModuleStoreTestCase, LoginEnrollmentTestCase):
def test_save_grade_success(self): def test_save_grade_success(self):
data = { data = {
'rubric_scores[]': [0, 0], 'rubric_scores[]': [0, 0],
'location': self.location, 'location': self.location_string,
'submission_id': 1, 'submission_id': 1,
'submission_key': 'fake key', 'submission_key': 'fake key',
'score': 2, 'score': 2,
...@@ -342,7 +342,7 @@ class TestPeerGradingService(ModuleStoreTestCase, LoginEnrollmentTestCase): ...@@ -342,7 +342,7 @@ class TestPeerGradingService(ModuleStoreTestCase, LoginEnrollmentTestCase):
self.assertTrue(d['error'].find('Missing required keys:') > -1) self.assertTrue(d['error'].find('Missing required keys:') > -1)
def test_is_calibrated_success(self): def test_is_calibrated_success(self):
data = {'location': self.location} data = {'location': self.location_string}
response = self.peer_module.is_student_calibrated(data) response = self.peer_module.is_student_calibrated(data)
self.assertTrue(response['success']) self.assertTrue(response['success'])
...@@ -355,7 +355,7 @@ class TestPeerGradingService(ModuleStoreTestCase, LoginEnrollmentTestCase): ...@@ -355,7 +355,7 @@ class TestPeerGradingService(ModuleStoreTestCase, LoginEnrollmentTestCase):
self.assertFalse('calibrated' in response) self.assertFalse('calibrated' in response)
def test_show_calibration_essay_success(self): def test_show_calibration_essay_success(self):
data = {'location': self.location} data = {'location': self.location_string}
response = self.peer_module.show_calibration_essay(data) response = self.peer_module.show_calibration_essay(data)
...@@ -376,7 +376,7 @@ class TestPeerGradingService(ModuleStoreTestCase, LoginEnrollmentTestCase): ...@@ -376,7 +376,7 @@ class TestPeerGradingService(ModuleStoreTestCase, LoginEnrollmentTestCase):
def test_save_calibration_essay_success(self): def test_save_calibration_essay_success(self):
data = { data = {
'rubric_scores[]': [0, 0], 'rubric_scores[]': [0, 0],
'location': self.location, 'location': self.location_string,
'submission_id': 1, 'submission_id': 1,
'submission_key': 'fake key', 'submission_key': 'fake key',
'score': 2, 'score': 2,
...@@ -410,7 +410,7 @@ class TestPeerGradingService(ModuleStoreTestCase, LoginEnrollmentTestCase): ...@@ -410,7 +410,7 @@ class TestPeerGradingService(ModuleStoreTestCase, LoginEnrollmentTestCase):
""" """
data = { data = {
'rubric_scores[]': [0, 0], 'rubric_scores[]': [0, 0],
'location': self.location, 'location': self.location_string,
'submission_id': 1, 'submission_id': 1,
'submission_key': 'fake key', 'submission_key': 'fake key',
'score': 2, 'score': 2,
......
import logging import logging
from django.conf import settings
from django.views.decorators.cache import cache_control from django.views.decorators.cache import cache_control
from edxmako.shortcuts import render_to_response from edxmako.shortcuts import render_to_response
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from student.models import unique_id_for_user
from courseware.courses import get_course_with_access from courseware.courses import get_course_with_access
from xmodule.open_ended_grading_classes.grading_service_module import GradingServiceError from xmodule.open_ended_grading_classes.grading_service_module import GradingServiceError
...@@ -20,11 +18,11 @@ from xmodule.modulestore import SlashSeparatedCourseKey ...@@ -20,11 +18,11 @@ from xmodule.modulestore import SlashSeparatedCourseKey
from xmodule.modulestore.exceptions import NoPathToItem from xmodule.modulestore.exceptions import NoPathToItem
from django.http import HttpResponse, Http404, HttpResponseRedirect from django.http import HttpResponse, Http404, HttpResponseRedirect
from edxmako.shortcuts import render_to_string
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from open_ended_grading.utils import (STAFF_ERROR_MESSAGE, STUDENT_ERROR_MESSAGE, from open_ended_grading.utils import (
StudentProblemList, generate_problem_url, create_controller_query_service) STAFF_ERROR_MESSAGE, StudentProblemList, generate_problem_url, create_controller_query_service
)
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
...@@ -68,9 +66,10 @@ def staff_grading(request, course_id): ...@@ -68,9 +66,10 @@ def staff_grading(request, course_id):
""" """
Show the instructor grading interface. Show the instructor grading interface.
""" """
course = get_course_with_access(request.user, 'staff', course_id) course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
course = get_course_with_access(request.user, 'staff', course_key)
ajax_url = _reverse_with_slash('staff_grading', course_id) ajax_url = _reverse_with_slash('staff_grading', course_key)
return render_to_response('instructor/staff_grading.html', { return render_to_response('instructor/staff_grading.html', {
'course': course, 'course': course,
...@@ -118,9 +117,9 @@ def peer_grading(request, course_id): ...@@ -118,9 +117,9 @@ def peer_grading(request, course_id):
When a student clicks on the "peer grading" button in the open ended interface, link them to a peer grading When a student clicks on the "peer grading" button in the open ended interface, link them to a peer grading
xmodule in the course. xmodule in the course.
''' '''
course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
#Get the current course #Get the current course
course = get_course_with_access(request.user, 'load', course_id) course = get_course_with_access(request.user, 'load', course_key)
found_module, problem_url = find_peer_grading_module(course) found_module, problem_url = find_peer_grading_module(course)
if not found_module: if not found_module:
...@@ -187,13 +186,11 @@ def flagged_problem_list(request, course_id): ...@@ -187,13 +186,11 @@ def flagged_problem_list(request, course_id):
''' '''
course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id) course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
course = get_course_with_access(request.user, 'staff', course_key) course = get_course_with_access(request.user, 'staff', course_key)
student_id = unique_id_for_user(request.user)
# call problem list service # call problem list service
success = False success = False
error_text = "" error_text = ""
problem_list = [] problem_list = []
base_course_url = reverse('courses')
# Make a service that can query edX ORA. # Make a service that can query edX ORA.
controller_qs = create_controller_query_service() controller_qs = create_controller_query_service()
......
...@@ -86,7 +86,7 @@ class User(models.Model): ...@@ -86,7 +86,7 @@ class User(models.Model):
if not self.course_id: if not self.course_id:
raise CommentClientRequestError("Must provide course_id when retrieving active threads for the user") raise CommentClientRequestError("Must provide course_id when retrieving active threads for the user")
url = _url_for_user_active_threads(self.id) url = _url_for_user_active_threads(self.id)
params = {'course_id': self.course_id} params = {'course_id': self.course_id.to_deprecated_string()}
params = merge_dict(params, query_params) params = merge_dict(params, query_params)
response = perform_request( response = perform_request(
'get', 'get',
...@@ -102,7 +102,7 @@ class User(models.Model): ...@@ -102,7 +102,7 @@ class User(models.Model):
if not self.course_id: if not self.course_id:
raise CommentClientRequestError("Must provide course_id when retrieving subscribed threads for the user") raise CommentClientRequestError("Must provide course_id when retrieving subscribed threads for the user")
url = _url_for_user_subscribed_threads(self.id) url = _url_for_user_subscribed_threads(self.id)
params = {'course_id': self.course_id} params = {'course_id': self.course_id.to_deprecated_string()}
params = merge_dict(params, query_params) params = merge_dict(params, query_params)
response = perform_request( response = perform_request(
'get', 'get',
...@@ -118,7 +118,7 @@ class User(models.Model): ...@@ -118,7 +118,7 @@ class User(models.Model):
url = self.url(action='get', params=self.attributes) url = self.url(action='get', params=self.attributes)
retrieve_params = self.default_retrieve_params retrieve_params = self.default_retrieve_params
if self.attributes.get('course_id'): if self.attributes.get('course_id'):
retrieve_params['course_id'] = self.course_id retrieve_params['course_id'] = self.course_id.to_deprecated_string()
try: try:
response = perform_request( response = perform_request(
'get', 'get',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment