Commit 297d77d5 by Braden MacDonald

Four improvements to upgrade script:

1. Better error handling/messages
2. Fix url_name inconsistencies (while preserving student data)
3. Auto-publish new version if previous one was published
4. Detect mentoring v1 blocks and only change them
parent b53159df
......@@ -13,6 +13,8 @@ from lxml import etree
from mentoring import MentoringBlock
from StringIO import StringIO
import sys
from courseware.models import StudentModule
from xmodule.modulestore.exceptions import DuplicateItemError
from .studio_xml_utils import studio_update_from_node
from .xml_changes import convert_xml_v1_to_v2
......@@ -24,7 +26,7 @@ def upgrade_block(block):
"""
assert isinstance(block, MentoringBlock)
assert bool(block.xml_content) # If it's a v1 block it will have xml_content
store = block.runtime.modulestore
xml_content_str = block.xml_content
parser = etree.XMLParser(remove_blank_text=True)
root = etree.parse(StringIO(xml_content_str), parser=parser).getroot()
......@@ -50,14 +52,63 @@ def upgrade_block(block):
# Save the xml_content to make this processes rerunnable, in case it doesn't work correctly the first time.
root.attrib["xml_content"] = xml_content_str
# Was block already published?
parent = block.get_parent()
parent_was_published = not store.has_changes(parent)
# If the block has a url_name attribute that doesn't match Studio's url_name, fix that:
delete_on_success = None
if "url_name" in root.attrib:
url_name = root.attrib.pop("url_name")
if block.url_name != url_name:
print(" ➔ This block has two conflicting url_name values set. Attempting to fix...")
# Fix the url_name by replacing the block with a blank block with the correct url_name
parent_children = parent.children
old_usage_id = block.location
index = parent_children.index(old_usage_id)
try:
new_block = store.create_item(
user_id=None,
course_key=block.location.course_key,
block_type="mentoring",
block_id=url_name,
fields={"xml_content": xml_content_str},
)
delete_on_success = block
parent_children[index] = new_block.location
parent.save()
store.update_item(parent, user_id=None)
block = new_block
print(" ➔ url_name changed to {}".format(url_name))
# Now we've fixed the block's url_name but in doing so we've disrupted the student data.
# Migrate it now:
student_data = StudentModule.objects.filter(module_state_key=old_usage_id)
num_entries = student_data.count()
if num_entries > 0:
print(" ➔ Migrating {} student records to new url_name".format(num_entries))
student_data.update(module_state_key=new_block.location)
except DuplicateItemError:
print(
"\n WARNING: The block with url_name '{}' doesn't match "
"the real url_name '{}' and auto repair failed.\n".format(
url_name, block.url_name
)
)
# Replace block with the new version and the new children:
studio_update_from_node(block, root)
if delete_on_success:
store.delete_item(delete_on_success.location, user_id=None)
if parent_was_published:
store.publish(parent.location, user_id=None)
if __name__ == '__main__':
# Disable some distracting overly-verbose warnings that we don't need:
for noisy_module in ('edx.modulestore', 'elasticsearch', 'urllib3.connectionpool'):
logging.getLogger(noisy_module).setLevel(logging.ERROR)
from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import CourseKey
from xmodule.modulestore.django import modulestore
......@@ -66,18 +117,20 @@ if __name__ == '__main__':
print("┗━━━━━━━━━━━━━━━━━━━━━━━━━━┛")
try:
course_id = sys.argv[1]
except IndexError:
course_id = CourseKey.from_string(sys.argv[1])
except (IndexError, InvalidKeyError):
sys.exit("Need a course ID argument like 'HarvardX/GSE1.1x/3T2014' or 'course-v1:HarvardX+B101+2015'")
store = modulestore()
course = store.get_course(CourseKey.from_string(course_id))
course = store.get_course(course_id)
if course is None:
sys.exit(u"Course '{}' not found.".format(unicode(course_id)))
print(" ➔ Found course: {}".format(course.display_name))
print(" ➔ Searching for mentoring blocks")
blocks_found = []
def find_mentoring_blocks(block):
if isinstance(block, MentoringBlock):
if isinstance(block, MentoringBlock) and block.xml_content: # If it's a v1 block it will have xml_content
blocks_found.append(block.scope_ids.usage_id)
elif block.has_children:
for child_id in block.children:
......
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