Commit d2b8b4f0 by zubiar-arbi Committed by zubair-arbi

remove location of item from loc_mapper and cache

parent 97d587c4
...@@ -125,6 +125,9 @@ def xblock_handler(request, tag=None, package_id=None, branch=None, version_guid ...@@ -125,6 +125,9 @@ def xblock_handler(request, tag=None, package_id=None, branch=None, version_guid
delete_children = str_to_bool(request.REQUEST.get('recurse', 'False')) delete_children = str_to_bool(request.REQUEST.get('recurse', 'False'))
delete_all_versions = str_to_bool(request.REQUEST.get('all_versions', 'False')) delete_all_versions = str_to_bool(request.REQUEST.get('all_versions', 'False'))
# delete item from loc_mapper and cache
loc_mapper().delete_item_mapping(locator, old_location)
return _delete_item_at_location(old_location, delete_children, delete_all_versions, request.user) return _delete_item_at_location(old_location, delete_children, delete_all_versions, request.user)
else: # Since we have a package_id, we are updating an existing xblock. else: # Since we have a package_id, we are updating an existing xblock.
if block == 'handouts' and old_location is None: if block == 'handouts' and old_location is None:
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
import json import json
from datetime import datetime from datetime import datetime
import ddt import ddt
from uuid import uuid4
from mock import patch from mock import patch
from pytz import UTC from pytz import UTC
...@@ -56,7 +57,10 @@ class ItemTest(CourseTestCase): ...@@ -56,7 +57,10 @@ class ItemTest(CourseTestCase):
parsed = json.loads(response.content) parsed = json.loads(response.content)
return parsed['locator'] return parsed['locator']
def create_xblock(self, parent_locator=None, display_name=None, category=None, boilerplate=None): def create_xblock(self, parent_locator=None, display_name=None, category=None, boilerplate=None, type=None):
"""
Create xblock
"""
data = { data = {
'parent_locator': self.unicode_locator if parent_locator is None else parent_locator, 'parent_locator': self.unicode_locator if parent_locator is None else parent_locator,
'category': category 'category': category
...@@ -65,6 +69,8 @@ class ItemTest(CourseTestCase): ...@@ -65,6 +69,8 @@ class ItemTest(CourseTestCase):
data['display_name'] = display_name data['display_name'] = display_name
if boilerplate is not None: if boilerplate is not None:
data['boilerplate'] = boilerplate data['boilerplate'] = boilerplate
if type is not None:
data['type'] = type
return self.client.ajax_post('/xblock', json.dumps(data)) return self.client.ajax_post('/xblock', json.dumps(data))
...@@ -673,6 +679,43 @@ class TestEditItem(ItemTest): ...@@ -673,6 +679,43 @@ class TestEditItem(ItemTest):
draft = self.get_item_from_modulestore(self.problem_locator, True) draft = self.get_item_from_modulestore(self.problem_locator, True)
self.assertNotEqual(draft.data, published.data) self.assertNotEqual(draft.data, published.data)
def test_create_delete_discussion_components(self):
"""
Create discussion components and test that they are deleted from loc_mapper on delete
"""
# Test that creating two discussion components with first 3 digit same of uuid will have
# different block_ids (locators)
with patch("uuid.UUID.hex", '987' + uuid4().hex[3:]):
resp = self.create_xblock(parent_locator=self.seq_locator, category='discussion', type='discussion')
self.assertEqual(resp.status_code, 200)
discussion_1_locator = self.response_locator(resp)
discussion_1_draft = self.get_item_from_modulestore(discussion_1_locator, True)
self.assertIsNotNone(discussion_1_draft)
with patch("uuid.UUID.hex", '987' + uuid4().hex[3:]):
resp = self.create_xblock(parent_locator=self.seq_locator, category='discussion', type='discussion')
self.assertEqual(resp.status_code, 200)
discussion_2_locator = self.response_locator(resp)
discussion_2_draft = self.get_item_from_modulestore(discussion_2_locator, True)
self.assertIsNotNone(discussion_2_draft)
self.assertNotEqual(discussion_1_locator, discussion_2_locator)
# now delete first discussion component
resp = self.client.delete('/xblock/' + discussion_1_locator)
self.assertEqual(resp.status_code, 204)
# create a new discussion component and check that it has same locator as first discussion component
# but different id's
with patch("uuid.UUID.hex", '987' + uuid4().hex[3:]):
resp = self.create_xblock(parent_locator=self.seq_locator, category='discussion', type='discussion')
self.assertEqual(resp.status_code, 200)
discussion_3_locator = self.response_locator(resp)
discussion_3_draft = self.get_item_from_modulestore(discussion_3_locator, True)
self.assertIsNotNone(discussion_3_draft)
self.assertEqual(discussion_1_locator, discussion_3_locator)
self.assertNotEqual(discussion_1_draft.id, discussion_3_draft.id)
def test_publish_states_of_nested_xblocks(self): def test_publish_states_of_nested_xblocks(self):
""" Test publishing of a unit page containing a nested xblock """ """ Test publishing of a unit page containing a nested xblock """
......
...@@ -152,25 +152,9 @@ class LocMapperStore(object): ...@@ -152,25 +152,9 @@ class LocMapperStore(object):
if cached_value: if cached_value:
return cached_value return cached_value
maps = self.location_map.find(location_id) entry = self._get_map_entry_form_location(location_id, location, add_entry_if_missing)
maps = list(maps) if entry is None:
if len(maps) == 0: raise ItemNotFoundError(location)
if add_entry_if_missing:
# create a new map
course_location = location.replace(category='course', name=location_id['_id']['name'])
self.create_map_entry(course_location)
entry = self.location_map.find_one(location_id)
else:
raise ItemNotFoundError(location)
elif len(maps) == 1:
entry = maps[0]
else:
# find entry w/o name, if any; otherwise, pick arbitrary
entry = maps[0]
for item in maps:
if 'name' not in item['_id']:
entry = item
break
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))
if block_id is None: if block_id is None:
...@@ -209,6 +193,34 @@ class LocMapperStore(object): ...@@ -209,6 +193,34 @@ class LocMapperStore(object):
self._cache_location_map_entry(old_style_course_id, location, published_usage, draft_usage) self._cache_location_map_entry(old_style_course_id, location, published_usage, draft_usage)
return result return result
def _get_map_entry_form_location(self, location_id, location=None, add_entry_if_missing=False):
"""
Returns loc_mapper entry related to provided location_id
:param location: a Location pointing to a module
:param location_id: location id in form of SON dict for querying the mapping table if location is provided
:param add_entry_if_missing: a boolean as to whether to return none or to create an entry
"""
maps = self.location_map.find(location_id)
maps = list(maps)
entry = None
if len(maps) == 0:
if location is not None and add_entry_if_missing:
# create a new map
course_location = location.replace(category='course', name=location_id['_id']['name'])
self.create_map_entry(course_location)
entry = self.location_map.find_one(location_id)
elif len(maps) == 1:
entry = maps[0]
else:
# find entry w/o name, if any; otherwise, pick arbitrary
entry = maps[0]
for item in maps:
if 'name' not in item['_id']:
entry = item
break
return entry
def translate_locator_to_location(self, locator, get_course=False, lower_only=False): def translate_locator_to_location(self, locator, get_course=False, lower_only=False):
""" """
Returns an old style Location for the given Locator if there's an appropriate entry in the Returns an old style Location for the given Locator if there's an appropriate entry in the
...@@ -299,19 +311,10 @@ class LocMapperStore(object): ...@@ -299,19 +311,10 @@ class LocMapperStore(object):
location_id = self._interpret_location_course_id(old_style_course_id, location, lower_only) location_id = self._interpret_location_course_id(old_style_course_id, location, lower_only)
maps = self.location_map.find(location_id) entry = self._get_map_entry_form_location(location_id)
maps = list(maps) if entry is None:
if len(maps) == 0:
raise ItemNotFoundError(location) raise ItemNotFoundError(location)
elif len(maps) == 1:
entry = maps[0]
else:
# find entry w/o name, if any; otherwise, pick arbitrary
entry = maps[0]
for item in maps:
if 'name' not in item['_id']:
entry = item
break
published_course_locator = CourseLocator(package_id=entry['course_id'], branch=entry['prod_branch']) published_course_locator = CourseLocator(package_id=entry['course_id'], branch=entry['prod_branch'])
draft_course_locator = CourseLocator(package_id=entry['course_id'], branch=entry['draft_branch']) draft_course_locator = CourseLocator(package_id=entry['course_id'], branch=entry['draft_branch'])
self._cache_course_locator(old_style_course_id, published_course_locator, draft_course_locator) self._cache_course_locator(old_style_course_id, published_course_locator, draft_course_locator)
...@@ -338,6 +341,20 @@ class LocMapperStore(object): ...@@ -338,6 +341,20 @@ class LocMapperStore(object):
self.location_map.update(location_id, {'$set': {'block_map': block_map}}) self.location_map.update(location_id, {'$set': {'block_map': block_map}})
return block_id return block_id
def _remove_from_block_map(self, location, location_id, block_map):
"""
Remove the given location from the block_map and persist it
"""
encoded_location_name = self.encode_key_for_mongo(location.name)
if encoded_location_name in block_map.keys():
map_entry = block_map[encoded_location_name]
if location.category in map_entry:
if len(map_entry) == 1:
del block_map[encoded_location_name]
else:
del map_entry[location.category]
self.location_map.update(location_id, {'$set': {'block_map': block_map}})
def _interpret_location_course_id(self, course_id, location, lower_only=False): def _interpret_location_course_id(self, course_id, location, lower_only=False):
""" """
Take the old style course id (org/course/run) and return a dict w/ a SON for querying the mapping table. Take the old style course id (org/course/run) and return a dict w/ a SON for querying the mapping table.
...@@ -531,3 +548,17 @@ class LocMapperStore(object): ...@@ -531,3 +548,17 @@ class LocMapperStore(object):
delete_keys.append(u'{}+{}'.format(old_course_id, location.url())) delete_keys.append(u'{}+{}'.format(old_course_id, location.url()))
delete_keys.append(old_course_id) delete_keys.append(old_course_id)
self.cache.delete_many(delete_keys) self.cache.delete_many(delete_keys)
def delete_item_mapping(self, locator, location):
"""
Delete item from loc_mapper and cache
:param locator: a BlockUsageLocator
:param location: a Location pointing to a module
"""
course_location = self.translate_locator_to_location(locator, get_course=True)
location_id = self._interpret_location_course_id(course_location.course_id, location)
entry = self._get_map_entry_form_location(location_id)
if entry is not None:
self._remove_from_block_map(location, location_id, entry['block_map'])
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