From a6663eb29d5bec4a412ffac2e20bb5407a0ab068 Mon Sep 17 00:00:00 2001
From: Irtaza Akram
Date: Mon, 30 Mar 2026 15:11:17 +0500
Subject: [PATCH 1/2] fix: remove XModuleMixin legacy attibutes from
annotatable, html & problem block
---
xblocks_contrib/annotatable/annotatable.py | 21 +----
.../annotatable/tests/test_annotatable.py | 2 +-
xblocks_contrib/html/html.py | 30 ++-----
xblocks_contrib/problem/capa/responsetypes.py | 2 +-
xblocks_contrib/problem/capa/tests/helpers.py | 2 +-
.../capa/tests/test_xqueue_submission.py | 2 +
xblocks_contrib/problem/capa/util.py | 2 +-
.../problem/capa/xqueue_submission.py | 4 +-
xblocks_contrib/problem/capa_block.py | 85 +++++++------------
xblocks_contrib/problem/tests/__init__.py | 4 +-
.../problem/tests/test_capa_block.py | 10 ++-
11 files changed, 56 insertions(+), 108 deletions(-)
diff --git a/xblocks_contrib/annotatable/annotatable.py b/xblocks_contrib/annotatable/annotatable.py
index 0c2c8755..2c5ebfd8 100644
--- a/xblocks_contrib/annotatable/annotatable.py
+++ b/xblocks_contrib/annotatable/annotatable.py
@@ -12,7 +12,6 @@
import markupsafe
from django.utils.translation import gettext_noop as _
from lxml import etree
-from opaque_keys.edx.keys import UsageKey
from web_fragments.fragment import Fragment
from xblock.core import XBlock
from xblock.fields import Scope, String, XMLString
@@ -96,18 +95,6 @@ class AnnotatableBlock(LegacyXmlMixin, XBlock):
# List of supported highlight colors for annotations
HIGHLIGHT_COLORS = ["yellow", "orange", "purple", "blue", "green"]
- @property
- def location(self):
- return self.scope_ids.usage_id
-
- @location.setter
- def location(self, value):
- assert isinstance(value, UsageKey)
- self.scope_ids = self.scope_ids._replace(
- def_id=value, # Note: assigning a UsageKey as def_id is OK in old mongo / import system but wrong in split
- usage_id=value,
- )
-
def _get_annotation_class_attr(self, index, el): # pylint: disable=unused-argument
"""Returns a dict with the CSS class attribute to set on the annotation
and an XML key to delete from the element.
@@ -279,8 +266,8 @@ def definition_to_xml(self, resource_fs):
if not self.data:
log.warning(
"Could not serialize %s: No XBlock installed for '%s' tag.",
- self.location,
- self.location.block_type,
+ self.usage_key,
+ self.usage_key.block_type,
)
return None
@@ -297,6 +284,6 @@ def definition_to_xml(self, resource_fs):
"Context: '{context}'"
).format(
context=lines[line - 1][offset - 40:offset + 40],
- loc=self.location,
+ loc=self.usage_key,
)
- raise SerializationError(self.location, msg) from err
+ raise SerializationError(self.usage_key, msg) from err
diff --git a/xblocks_contrib/annotatable/tests/test_annotatable.py b/xblocks_contrib/annotatable/tests/test_annotatable.py
index 3251bbb8..d494ba4e 100644
--- a/xblocks_contrib/annotatable/tests/test_annotatable.py
+++ b/xblocks_contrib/annotatable/tests/test_annotatable.py
@@ -35,7 +35,7 @@ class AnnotatableBlockTestCase(unittest.TestCase):
def setUp(self):
super().setUp()
runtime = TestRuntime()
- scope_ids = ScopeIds("user_id", "block_type", "block_id", "course_id")
+ scope_ids = ScopeIds("user_id", "block_type", "block_id", "context_key")
field_data = DictFieldData({"data": self.sample_xml})
self.annotatable = AnnotatableBlock(runtime, field_data, scope_ids)
diff --git a/xblocks_contrib/html/html.py b/xblocks_contrib/html/html.py
index 7d3c6301..537b83d2 100644
--- a/xblocks_contrib/html/html.py
+++ b/xblocks_contrib/html/html.py
@@ -15,7 +15,7 @@
from django.utils.translation import gettext_noop as _
from fs.errors import ResourceNotFound
from lxml import etree
-from opaque_keys.edx.keys import CourseKey, UsageKey
+from opaque_keys.edx.keys import CourseKey
from opaque_keys.edx.locator import LibraryLocatorV2
from path import Path as path
from web_fragments.fragment import Fragment
@@ -187,26 +187,6 @@ class HtmlBlockMixin(LegacyXmlMixin, XBlock):
show_in_read_only_mode = True
icon_class = "other"
- @property
- def category(self):
- return self.scope_ids.block_type
-
- @property
- def location(self):
- return self.scope_ids.usage_id
-
- @location.setter
- def location(self, value):
- assert isinstance(value, UsageKey)
- self.scope_ids = self.scope_ids._replace(
- def_id=value, # Note: assigning a UsageKey as def_id is OK in old mongo / import system but wrong in split
- usage_id=value,
- )
-
- @property
- def url_name(self):
- return self.location.block_id
-
@property
def xblock_kvs(self):
"""
@@ -264,7 +244,7 @@ def get_html(self):
data = data.replace("%%USER_EMAIL%%", email)
# The course ID replacement is always safe to run.
- data = data.replace("%%COURSE_ID%%", str(self.scope_ids.usage_id.context_key))
+ data = data.replace("%%COURSE_ID%%", str(self.context_key))
return data
def studio_view(self, context=None):
@@ -324,7 +304,7 @@ def get_context(self):
"module": self,
"editable_metadata_fields": self.editable_metadata_fields,
"data": self.data,
- "base_asset_url": self.get_base_url_path_for_course_assets(self.location.course_key),
+ "base_asset_url": self.get_base_url_path_for_course_assets(self.context_key),
"enable_latex_compiler": self.use_latex_compiler,
"editor": self.editor,
}
@@ -545,8 +525,8 @@ def definition_to_xml(self, resource_fs):
"""
# Write html to file, return an empty tag
- pathname = name_to_pathname(self.url_name)
- filepath = "{category}/{pathname}.html".format(category=self.category, pathname=pathname)
+ pathname = name_to_pathname(self.usage_key.block_id)
+ filepath = "{category}/{pathname}.html".format(category=self.usage_key.block_type, pathname=pathname)
resource_fs.makedirs(os.path.dirname(filepath), recreate=True)
with resource_fs.open(filepath, "wb") as filestream:
diff --git a/xblocks_contrib/problem/capa/responsetypes.py b/xblocks_contrib/problem/capa/responsetypes.py
index b304a4fc..feacffa5 100644
--- a/xblocks_contrib/problem/capa/responsetypes.py
+++ b/xblocks_contrib/problem/capa/responsetypes.py
@@ -392,7 +392,7 @@ def make_hint_div( # pylint: disable=too-many-positional-arguments,too-many-arg
# This is the "feedback hint" event
event_info = {}
- event_info["module_id"] = str(self.capa_block.location)
+ event_info["module_id"] = str(self.capa_block.usage_key)
event_info["problem_part_id"] = self.id
event_info["trigger_type"] = "single" # maybe be overwritten by log_extra
event_info["hint_label"] = label
diff --git a/xblocks_contrib/problem/capa/tests/helpers.py b/xblocks_contrib/problem/capa/tests/helpers.py
index a91aabf1..2ce9d144 100644
--- a/xblocks_contrib/problem/capa/tests/helpers.py
+++ b/xblocks_contrib/problem/capa/tests/helpers.py
@@ -90,7 +90,7 @@ def mock_location_text(self): # pylint: disable=unused-argument
return "i4x://Foo/bar/mock/abc"
capa_block = Mock()
- capa_block.location.__str__ = mock_location_text
+ capa_block.usage_key.__str__ = mock_location_text
# The following comes into existence by virtue of being called
# capa_block.runtime.publish
return capa_block
diff --git a/xblocks_contrib/problem/capa/tests/test_xqueue_submission.py b/xblocks_contrib/problem/capa/tests/test_xqueue_submission.py
index 7f341b13..e021b745 100644
--- a/xblocks_contrib/problem/capa/tests/test_xqueue_submission.py
+++ b/xblocks_contrib/problem/capa/tests/test_xqueue_submission.py
@@ -19,6 +19,8 @@ def xqueue_service():
"""
location = BlockUsageLocator(CourseLocator("test_org", "test_course", "test_run"), "problem", "ExampleProblem")
block = Mock(scope_ids=ScopeIds("user1", "problem", location, location))
+ block.usage_key = location
+ block.context_key = location.course_key
block.max_score = Mock(return_value=10)
return XQueueInterfaceSubmission(block)
diff --git a/xblocks_contrib/problem/capa/util.py b/xblocks_contrib/problem/capa/util.py
index 8533991b..c41c52c2 100644
--- a/xblocks_contrib/problem/capa/util.py
+++ b/xblocks_contrib/problem/capa/util.py
@@ -239,7 +239,7 @@ def get_course_id_from_capa_block(capa_block):
if not capa_block:
return None
try:
- return str(capa_block.scope_ids.usage_id.course_key)
+ return str(capa_block.context_key)
except (AttributeError, TypeError):
# AttributeError:
# If the capa block lacks scope ids or has unexpected scope ids, we
diff --git a/xblocks_contrib/problem/capa/xqueue_submission.py b/xblocks_contrib/problem/capa/xqueue_submission.py
index 2c571282..98ef8bdf 100644
--- a/xblocks_contrib/problem/capa/xqueue_submission.py
+++ b/xblocks_contrib/problem/capa/xqueue_submission.py
@@ -55,11 +55,11 @@ def get_submission_params(self, header, payload):
if not self.block:
raise GetSubmissionParamsError()
- course_id = str(self.block.scope_ids.usage_id.context_key)
+ course_id = str(self.block.context_key)
item_type = self.block.scope_ids.block_type
points_possible = self.block.max_score()
- item_id = str(self.block.scope_ids.usage_id)
+ item_id = str(self.block.usage_key)
try:
grader_payload = self._parse_json(payload["grader_payload"], "grader_payload")
diff --git a/xblocks_contrib/problem/capa_block.py b/xblocks_contrib/problem/capa_block.py
index 3e6f9a84..e43b137a 100644
--- a/xblocks_contrib/problem/capa_block.py
+++ b/xblocks_contrib/problem/capa_block.py
@@ -23,7 +23,6 @@
from django.utils.encoding import smart_str
from django.utils.functional import cached_property
from lxml import etree
-from opaque_keys.edx.keys import UsageKey
from web_fragments.fragment import Fragment
from webob import Response
from webob.multidict import MultiDict
@@ -118,8 +117,8 @@ def definition_to_xml(self, resource_fs): # pylint: disable=unused-argument
if not self.data:
log.warning(
"Could not serialize %s: No XBlock installed for '%s' tag.",
- self.location,
- self.location.block_type,
+ self.usage_key,
+ self.usage_key.block_type,
)
return None
@@ -132,10 +131,10 @@ def definition_to_xml(self, resource_fs): # pylint: disable=unused-argument
lines = self.data.split("\n")
line, offset = err.position
msg = (
- f"Unable to create xml for block {self.location}. "
+ f"Unable to create xml for block {self.usage_key}. "
f"Context: '{lines[line - 1][offset - 40: offset + 40]}'"
)
- raise SerializationError(self.location, msg) from err
+ raise SerializationError(self.usage_key, msg) from err
@classmethod
def parse_xml_new_runtime(cls, node, runtime, keys):
@@ -170,28 +169,6 @@ class XModuleMixin(XBlock):
on XModule-style internals and remove this class.
"""
- @property
- def category(self):
- """Return the block type/category."""
- return self.scope_ids.block_type
-
- @property
- def location(self):
- """Return the usage key identifying this block instance."""
- return self.scope_ids.usage_id
-
- @location.setter
- def location(self, value):
- assert isinstance(value, UsageKey)
- self.scope_ids = self.scope_ids._replace(
- def_id=value, # Note: assigning a UsageKey as def_id is OK in old mongo / import system but wrong in split
- usage_id=value,
- )
-
- @property
- def url_name(self):
- return self.location.block_id
-
@property
def xblock_kvs(self):
"""
@@ -868,7 +845,7 @@ def handle_ajax(self, dispatch, data): # pylint: disable=too-many-locals
log.info(
"Unable to find data when dispatching %s to %s for user %s",
dispatch,
- self.scope_ids.usage_id,
+ self.usage_key,
self.scope_ids.user_id,
)
_, _, traceback_obj = sys.exc_info()
@@ -878,7 +855,7 @@ def handle_ajax(self, dispatch, data): # pylint: disable=too-many-locals
log.exception(
"Unknown error when dispatching %s to %s for user %s",
dispatch,
- self.scope_ids.usage_id,
+ self.usage_key,
self.scope_ids.user_id,
)
_, _, traceback_obj = sys.exc_info()
@@ -909,7 +886,7 @@ def display_name_with_default(self):
else fall back to problem category.
"""
if self.display_name is None or not self.display_name.strip():
- return self.location.block_type
+ return self.usage_key.block_type
return self.display_name
@@ -1070,7 +1047,7 @@ def max_score(self):
try:
lcp = LoncapaProblem(
problem_text=self.data,
- id=self.location.html_id(),
+ id=self.usage_key.html_id(),
capa_system=capa_system,
capa_block=self,
state={},
@@ -1078,7 +1055,7 @@ def max_score(self):
minimal_init=True,
)
except responsetypes.LoncapaProblemError:
- log.exception("LcpFatalError for block %s while getting max score", str(self.location))
+ log.exception("LcpFatalError for block %s while getting max score", str(self.usage_key))
maximum_score = 0
else:
maximum_score = lcp.get_max_score()
@@ -1104,7 +1081,7 @@ def generate_report_data(self, user_state_iterator, limit_responses=None):
"Answer ID": "98e6a8e915904d5389821a94e48babcf_10_1"
})
"""
- if self.category != "problem":
+ if self.scope_ids.block_type != "problem":
raise NotImplementedError()
if limit_responses == 0:
@@ -1138,7 +1115,7 @@ def generate_report_data(self, user_state_iterator, limit_responses=None):
try:
lcp = LoncapaProblem(
problem_text=self.data,
- id=self.location.html_id(),
+ id=self.usage_key.html_id(),
capa_system=capa_system,
# We choose to run without a fully initialized CapaModule
capa_block=None,
@@ -1184,8 +1161,8 @@ def generate_report_data(self, user_state_iterator, limit_responses=None):
# Capture a backtrace for errors from failed loncapa problems
log.exception(
"An error occurred generating a problem report on course %s, problem %s, and student %s",
- self.scope_ids.usage_id.course_key,
- self.scope_ids.usage_id,
+ self.context_key,
+ self.usage_key,
self.scope_ids.user_id,
)
# Also input error in report
@@ -1235,7 +1212,7 @@ def lcp(self): # pylint: disable=method-hidden
try:
lcp = self.new_lcp(self.get_state_for_lcp())
except Exception as err:
- msg = f"cannot create LoncapaProblem {str(self.location)}: {err}"
+ msg = f"cannot create LoncapaProblem {str(self.usage_key)}: {err}"
raise LoncapaProblemError(msg).with_traceback(sys.exc_info()[2])
if self.score is None:
@@ -1253,7 +1230,7 @@ def choose_new_seed(self):
elif self.rerandomize == RANDOMIZATION.PER_STUDENT:
user_id = self.runtime.service(self, "user").get_current_user().opt_attrs.get(ATTR_KEY_USER_ID) or 0
# see comment on randomization_bin
- self.seed = randomization_bin(user_id, str(self.location).encode("utf-8"))
+ self.seed = randomization_bin(user_id, str(self.usage_key).encode("utf-8"))
else:
self.seed = struct.unpack("i", os.urandom(4))[0]
@@ -1295,7 +1272,7 @@ def new_lcp(self, state, text=None):
return LoncapaProblem(
problem_text=text,
- id=self.location.html_id(),
+ id=self.usage_key.html_id(),
state=state,
seed=self.get_seed(),
capa_system=capa_system,
@@ -1385,8 +1362,8 @@ def get_html(self):
return render_to_string(
"problem_ajax.html",
{
- "element_id": self.location.html_id(),
- "id": str(self.location),
+ "element_id": self.usage_key.html_id(),
+ "id": str(self.usage_key),
"ajax_url": self.ajax_url,
"current_score": curr_score,
"total_possible": total_possible,
@@ -1400,7 +1377,7 @@ def handle_fatal_lcp_error(self, error):
"""
Log a fatal LoncapaProblem error and return an HTML message for display to the user.
"""
- log.exception("LcpFatalError Encountered for %s", str(self.location))
+ log.exception("LcpFatalError Encountered for %s", str(self.usage_key))
if error:
return HTML('Error formatting HTML for problem:
{msg}
').format(
msg=str(error)
@@ -1516,12 +1493,12 @@ def handle_problem_html_error(self, err):
`err` is the Exception encountered while rendering the problem HTML.
"""
problem_display_name = self.display_name_with_default
- problem_location = str(self.location)
+ problem_location = str(self.usage_key)
log.exception("ProblemGetHtmlError: %r, %r, %s", problem_display_name, problem_location, str(err))
if self.debug:
msg = HTML("[courseware.capa.capa_block] Failed to generate HTML for problem {url}").format(
- url=str(self.location)
+ url=str(self.usage_key)
)
msg += HTML("Error:
{msg}").format(msg=str(err))
msg += HTML("{tb}").format(tb=traceback.format_exc())
@@ -1642,7 +1619,7 @@ def get_demand_hint(self, hint_index):
# Log this demand-hint request. Note that this only logs the last hint requested (although now
# all previously shown hints are still displayed).
event_info = {}
- event_info["module_id"] = str(self.location)
+ event_info["module_id"] = str(self.usage_key)
event_info["hint_index"] = hint_index
event_info["hint_len"] = len(demand_hints)
event_info["hint_text"] = get_inner_html_from_xpath(demand_hints[hint_index])
@@ -1711,8 +1688,8 @@ def get_problem_html(self, encapsulate=True, submit_notification=False): # pyli
context = {
"problem": content,
- "id": str(self.location),
- "short_id": self.location.html_id(),
+ "id": str(self.usage_key),
+ "short_id": self.usage_key.html_id(),
"submit_button": submit_button,
"submit_button_submitting": submit_button_submitting,
"should_enable_submit_button": should_enable_submit_button,
@@ -1735,7 +1712,7 @@ def get_problem_html(self, encapsulate=True, submit_notification=False): # pyli
if encapsulate:
html = HTML('{html}
').format(
- id=self.location.html_id(), ajax_url=self.ajax_url, html=HTML(html)
+ id=self.usage_key.html_id(), ajax_url=self.ajax_url, html=HTML(html)
)
# Now do all the substitutions which the LMS block_render normally does, but
@@ -2017,7 +1994,7 @@ def get_answer(self, _data):
(and also screen reader text).
"""
event_info = {}
- event_info["problem_id"] = str(self.location)
+ event_info["problem_id"] = str(self.usage_key)
self.publish_unmasked("showanswer", event_info)
if not self.answer_available():
raise NotFoundError("Answer is not available")
@@ -2166,7 +2143,7 @@ def submit_problem( # pylint: disable=too-many-statements,too-many-branches,too
"""
event_info = {}
event_info["state"] = self.lcp.get_state()
- event_info["problem_id"] = str(self.location)
+ event_info["problem_id"] = str(self.usage_key)
self.lcp.has_saved_answers = False
answers = self.make_dict_of_responses(data)
@@ -2185,7 +2162,7 @@ def submit_problem( # pylint: disable=too-many-statements,too-many-branches,too
if self.closed():
log.error(
"ProblemClosedError: Problem %s, close date: %s, due:%s, is_past_due: %s, attempts: %s/%s,",
- str(self.location),
+ str(self.usage_key),
self.close_date,
self.due,
self.is_past_due(),
@@ -2504,7 +2481,7 @@ def save_problem(self, data):
"""
event_info = {}
event_info["state"] = self.lcp.get_state()
- event_info["problem_id"] = str(self.location)
+ event_info["problem_id"] = str(self.usage_key)
answers = self.make_dict_of_responses(data)
event_info["answers"] = answers
@@ -2556,7 +2533,7 @@ def reset_problem(self, _data):
"""
event_info = {}
event_info["old_state"] = self.lcp.get_state()
- event_info["problem_id"] = str(self.location)
+ event_info["problem_id"] = str(self.usage_key)
_ = self.runtime.service(self, "i18n").gettext
if self.closed():
@@ -2620,7 +2597,7 @@ def rescore(self, only_if_higher=False):
Returns the error messages for exceptions occurring while performing
the rescoring, rather than throwing them.
"""
- event_info = {"state": self.lcp.get_state(), "problem_id": str(self.location)}
+ event_info = {"state": self.lcp.get_state(), "problem_id": str(self.usage_key)}
_ = self.runtime.service(self, "i18n").gettext
diff --git a/xblocks_contrib/problem/tests/__init__.py b/xblocks_contrib/problem/tests/__init__.py
index 5327df44..241b0171 100644
--- a/xblocks_contrib/problem/tests/__init__.py
+++ b/xblocks_contrib/problem/tests/__init__.py
@@ -172,11 +172,11 @@ def handler_url( # pylint: disable=arguments-differ,too-many-positional-argumen
self, block, handler_name, suffix="", query="", thirdparty=False
):
"""Mock handler URL generation to look like edx-platform URLs."""
- return f"http://testserver/xblock/{block.scope_ids.usage_id}/{handler_name}/{suffix}?{query}"
+ return f"http://testserver/xblock/{block.usage_key}/{handler_name}/{suffix}?{query}"
def local_resource_url(self, block, uri): # pylint: disable=arguments-differ
"""Mock local resource URL generation."""
- return f"resource/{block.scope_ids.usage_id}/{uri}"
+ return f"resource/{block.usage_key}/{uri}"
def publish(self, block, event_type, event_data): # pylint: disable=arguments-differ,unused-argument
return None
diff --git a/xblocks_contrib/problem/tests/test_capa_block.py b/xblocks_contrib/problem/tests/test_capa_block.py
index 00514ab5..da9a30c2 100644
--- a/xblocks_contrib/problem/tests/test_capa_block.py
+++ b/xblocks_contrib/problem/tests/test_capa_block.py
@@ -161,7 +161,7 @@ def create( # pylint: disable=too-many-arguments,too-many-positional-arguments
field_data["attempts"] = int(attempts)
system = get_test_system(
- course_id=location.course_key,
+ course_id=location.context_key,
user_is_staff=kwargs.get("user_is_staff", False),
render_template=render_template or Mock(return_value="Test Template HTML
"),
)
@@ -250,7 +250,9 @@ def test_import(self):
other_block = CapaFactory.create()
assert block.get_score().raw_earned == 0
- assert block.url_name != other_block.url_name, "Factory should be creating unique names for each problem"
+ assert (
+ block.usage_key.block_id != other_block.usage_key.block_id
+ ), "Factory should be creating unique names for each problem"
def test_correct(self):
"""
@@ -2464,7 +2466,7 @@ def test_demand_hint_logging(self):
mock_publish.assert_called_with(
block,
"edx.problem.hint.demandhint_displayed",
- {"hint_index": 0, "module_id": str(block.location), "hint_text": "Demand 1", "hint_len": 2},
+ {"hint_index": 0, "module_id": str(block.usage_key), "hint_text": "Demand 1", "hint_len": 2},
)
def test_input_state_consistency(self):
@@ -2867,7 +2869,7 @@ def test_problem_no_display_name(self, display_name, render_template):
block.get_problem_html()
render_args, _ = render_template.call_args
context = render_args[1]
- assert context["problem"]["name"] == block.location.block_type
+ assert context["problem"]["name"] == block.usage_key.block_type
@ddt.ddt
From 94cd66f30593f59f0c879770307c78aa2e18eea8 Mon Sep 17 00:00:00 2001
From: Irtaza Akram
Date: Mon, 30 Mar 2026 18:00:30 +0500
Subject: [PATCH 2/2] fix: test cases
---
xblocks_contrib/common/xml_utils.py | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/xblocks_contrib/common/xml_utils.py b/xblocks_contrib/common/xml_utils.py
index 3a4a3720..f5c5a079 100644
--- a/xblocks_contrib/common/xml_utils.py
+++ b/xblocks_contrib/common/xml_utils.py
@@ -139,7 +139,7 @@ def own_metadata(block: XBlock) -> dict[str, Any]:
except TypeError as exception:
exception_message = "{message}, Block-location:{location}, Field-name:{field_name}".format(
message=str(exception),
- location=str(block.location),
+ location=str(block.usage_key),
field_name=field.name
)
raise TypeError(exception_message) # lint-amnesty, pylint: disable=raise-missing-from
@@ -486,12 +486,12 @@ def add_xml_to_node(self, node):
aside.add_xml_to_node(aside_node)
xml_object.append(aside_node)
- not_to_clean_fields = self.metadata_to_not_to_clean.get(self.category, ())
+ not_to_clean_fields = self.metadata_to_not_to_clean.get(self.usage_key.block_type, ())
self.clean_metadata_from_xml(xml_object, excluded_fields=not_to_clean_fields)
# Set the tag on both nodes so we get the file path right.
- xml_object.tag = self.category
- node.tag = self.category
+ xml_object.tag = self.usage_key.block_type
+ node.tag = self.usage_key.block_type
# Add the non-inherited metadata
for attr in sorted(own_metadata(self)):
@@ -506,7 +506,7 @@ def add_xml_to_node(self, node):
logging.exception(
'Failed to serialize metadata attribute %s with value %s in module %s. '
'This could mean data loss!!!',
- attr, val, self.url_name
+ attr, val, self.usage_key.block_id
)
for key, value in self.xml_attributes.items():
@@ -515,8 +515,8 @@ def add_xml_to_node(self, node):
if self.export_to_file():
# Write the definition to a file
- url_path = name_to_pathname(self.url_name)
- filepath = self._format_filepath(self.category, url_path)
+ url_path = name_to_pathname(self.usage_key.block_id)
+ filepath = self._format_filepath(self.usage_key.block_type, url_path)
self.runtime.export_fs.makedirs(os.path.dirname(filepath), recreate=True)
with self.runtime.export_fs.open(filepath, 'wb') as fileobj:
ElementTree(xml_object).write(fileobj, pretty_print=True, encoding='utf-8')
@@ -531,7 +531,7 @@ def add_xml_to_node(self, node):
# Do not override an existing value for the course.
if not node.get('url_name'):
- node.set('url_name', self.url_name)
+ node.set('url_name', self.usage_key.block_id)
# We do not need to cater the `course` category here in xblocks_contrib,
# because course export is handled in the edx-platform code.