fixup! Test both split and old mongo
This commit is contained in:
@@ -9,10 +9,10 @@ from django.contrib.auth.models import User
|
||||
from xmodule.contentstore.django import _CONTENTSTORE
|
||||
from xmodule.modulestore.django import modulestore, clear_existing_modulestores
|
||||
from xmodule.modulestore import ModuleStoreEnum
|
||||
from collections import namedtuple
|
||||
import datetime
|
||||
import pytz
|
||||
from xmodule.tabs import CoursewareTab, CourseInfoTab, StaticTab, DiscussionTab, ProgressTab, WikiTab
|
||||
from xmodule.modulestore.tests.sample_courses import default_block_info_tree, TOY_BLOCK_INFO_TREE
|
||||
|
||||
|
||||
def mixed_store_config(data_dir, mappings):
|
||||
@@ -86,7 +86,6 @@ def split_mongo_store_config(data_dir):
|
||||
'default_class': 'xmodule.raw_module.RawDescriptor',
|
||||
'fs_root': data_dir,
|
||||
'render_template': 'edxmako.shortcuts.render_to_string',
|
||||
# ??? does this & draft need xblock_mixins?
|
||||
}
|
||||
|
||||
store = {
|
||||
@@ -123,188 +122,7 @@ def xml_store_config(data_dir):
|
||||
return store
|
||||
|
||||
|
||||
# used to create course subtrees in ModuleStoreTestCase.create_test_course
|
||||
# adds to self properties w/ the given block_id which hold the UsageKey for easy retrieval.
|
||||
# fields is a dictionary of keys and values. sub_tree is a collection of BlockInfo
|
||||
BlockInfo = namedtuple('BlockInfo', 'block_id, category, fields, sub_tree')
|
||||
default_block_info_tree = [
|
||||
BlockInfo(
|
||||
'chapter_x', 'chapter', {}, [
|
||||
BlockInfo(
|
||||
'sequential_x1', 'sequential', {}, [
|
||||
BlockInfo(
|
||||
'vertical_x1a', 'vertical', {}, [
|
||||
BlockInfo('problem_x1a_1', 'problem', {}, []),
|
||||
BlockInfo('problem_x1a_2', 'problem', {}, []),
|
||||
BlockInfo('problem_x1a_3', 'problem', {}, []),
|
||||
BlockInfo('html_x1a_1', 'html', {}, []),
|
||||
]
|
||||
)
|
||||
]
|
||||
)
|
||||
]
|
||||
),
|
||||
BlockInfo(
|
||||
'chapter_y', 'chapter', {}, [
|
||||
BlockInfo(
|
||||
'sequential_y1', 'sequential', {}, [
|
||||
BlockInfo(
|
||||
'vertical_y1a', 'vertical', {}, [
|
||||
BlockInfo('problem_y1a_1', 'problem', {}, []),
|
||||
BlockInfo('problem_y1a_2', 'problem', {}, []),
|
||||
BlockInfo('problem_y1a_3', 'problem', {}, []),
|
||||
]
|
||||
)
|
||||
]
|
||||
)
|
||||
]
|
||||
)
|
||||
]
|
||||
# equivalent to toy course in xml
|
||||
TOY_BLOCK_INFO_TREE = [
|
||||
BlockInfo(
|
||||
'Overview', "chapter", {"display_name" : "Overview"}, [
|
||||
BlockInfo(
|
||||
"Toy_Videos", "videosequence", {
|
||||
"xml_attributes": {"filename": ["", None]}, "display_name": "Toy Videos", "format": "Lecture Sequence"
|
||||
}, [
|
||||
BlockInfo(
|
||||
"secret:toylab", "html", {
|
||||
"data": "<b>Lab 2A: Superposition Experiment</b>\n\n<<<<<<< Updated upstream\n<p>Isn't the toy course great?</p>\n\n<p>Let's add some markup that uses non-ascii characters.\nFor example, we should be able to write words like encyclopædia, or foreign words like français.\nLooking beyond latin-1, we should handle math symbols: πr² ≤ ∞.\nAnd it shouldn't matter if we use entities or numeric codes — Ω ≠ π ≡ Ω ≠ π.\n</p>\n=======\n<p>Isn't the toy course great? — ≤</p>\n>>>>>>> Stashed changes\n",
|
||||
"xml_attributes": { "filename" : [ "html/secret/toylab.xml", "html/secret/toylab.xml" ] },
|
||||
"display_name" : "Toy lab"
|
||||
}, []
|
||||
),
|
||||
BlockInfo(
|
||||
"toyjumpto", "html", {
|
||||
"data" : "<a href=\"/jump_to_id/vertical_test\">This is a link to another page and some Chinese 四節比分和七年前</a> <p>Some more Chinese 四節比分和七年前</p>\n",
|
||||
"xml_attributes": { "filename" : [ "html/toyjumpto.xml", "html/toyjumpto.xml" ] }
|
||||
}, []),
|
||||
BlockInfo(
|
||||
"toyhtml", "html", {
|
||||
"data" : "<a href='/static/handouts/sample_handout.txt'>Sample</a>",
|
||||
"xml_attributes" : { "filename" : [ "html/toyhtml.xml", "html/toyhtml.xml" ] }
|
||||
}, []),
|
||||
BlockInfo(
|
||||
"nonportable", "html", {
|
||||
"data": "<a href=\"/static/foo.jpg\">link</a>\n",
|
||||
"xml_attributes" : { "filename" : [ "html/nonportable.xml", "html/nonportable.xml" ] }
|
||||
}, []),
|
||||
BlockInfo(
|
||||
"nonportable_link", "html", {
|
||||
"data": "<a href=\"/jump_to_id/nonportable_link\">link</a>\n\n",
|
||||
"xml_attributes": {"filename": ["html/nonportable_link.xml", "html/nonportable_link.xml"]}
|
||||
}, []),
|
||||
BlockInfo(
|
||||
"badlink", "html", {
|
||||
"data": "<img src=\"/static//file.jpg\" />\n",
|
||||
"xml_attributes" : { "filename" : [ "html/badlink.xml", "html/badlink.xml" ] }
|
||||
}, []),
|
||||
BlockInfo(
|
||||
"with_styling", "html", {
|
||||
"data": "<p style=\"font:italic bold 72px/30px Georgia, serif; color: red; \">Red text here</p>",
|
||||
"xml_attributes": {"filename": ["html/with_styling.xml", "html/with_styling.xml"]}
|
||||
}, []),
|
||||
BlockInfo(
|
||||
"just_img", "html", {
|
||||
"data": "<img src=\"/static/foo_bar.jpg\" />",
|
||||
"xml_attributes": {"filename": [ "html/just_img.xml", "html/just_img.xml" ] }
|
||||
}, []),
|
||||
BlockInfo(
|
||||
"Video_Resources", "video", {
|
||||
"youtube_id_1_0" : "1bK-WdDi6Qw", "display_name" : "Video Resources"
|
||||
}, []),
|
||||
]),
|
||||
BlockInfo(
|
||||
"Welcome", "video", {"data": "", "youtube_id_1_0": "p2Q6BrNhdh8", "display_name": "Welcome"}, []
|
||||
),
|
||||
BlockInfo(
|
||||
"video_123456789012", "video", {"data": "", "youtube_id_1_0": "p2Q6BrNhdh8", "display_name": "Test Video"}, []
|
||||
),
|
||||
BlockInfo(
|
||||
"video_4f66f493ac8f", "video", {"youtube_id_1_0": "p2Q6BrNhdh8"}, []
|
||||
)
|
||||
]
|
||||
),
|
||||
BlockInfo(
|
||||
"secret:magic", "chapter", {
|
||||
"xml_attributes": {"filename": [ "chapter/secret/magic.xml", "chapter/secret/magic.xml"]}
|
||||
}, [
|
||||
BlockInfo(
|
||||
"toyvideo", "video", {"youtube_id_1_0": "OEoXaMPEzfMA", "display_name": "toyvideo"}, []
|
||||
)
|
||||
]
|
||||
),
|
||||
BlockInfo(
|
||||
"poll_test", "chapter", {}, [
|
||||
BlockInfo(
|
||||
"T1_changemind_poll_foo", "poll_question", {
|
||||
"question": "<p>Have you changed your mind? ’</p>",
|
||||
"answers": [{"text": "Yes", "id": "yes"}, {"text": "No", "id": "no"}],
|
||||
"xml_attributes": {"reset": "false", "filename": ["", None]},
|
||||
"display_name": "Change your answer"
|
||||
}, []) ]
|
||||
),
|
||||
BlockInfo(
|
||||
"vertical_container", "chapter", {
|
||||
"xml_attributes": {"filename": ["chapter/vertical_container.xml", "chapter/vertical_container.xml"]}
|
||||
}, [
|
||||
BlockInfo("vertical_sequential", "sequential", {}, [
|
||||
BlockInfo("vertical_test", "vertical", {
|
||||
"xml_attributes": {"filename": ["vertical/vertical_test.xml", "vertical_test"]}
|
||||
}, [
|
||||
BlockInfo(
|
||||
"sample_video", "video", {
|
||||
"youtube_id_1_25": "AKqURZnYqpk",
|
||||
"youtube_id_0_75": "JMD_ifUUfsU",
|
||||
"youtube_id_1_0": "OEoXaMPEzfM",
|
||||
"display_name": "default",
|
||||
"youtube_id_1_5": "DYpADpL7jAY"
|
||||
}, []),
|
||||
BlockInfo(
|
||||
"separate_file_video", "video", {
|
||||
"youtube_id_1_25": "AKqURZnYqpk",
|
||||
"youtube_id_0_75": "JMD_ifUUfsU",
|
||||
"youtube_id_1_0": "OEoXaMPEzfM",
|
||||
"display_name": "default",
|
||||
"youtube_id_1_5": "DYpADpL7jAY"
|
||||
}, []),
|
||||
BlockInfo(
|
||||
"video_with_end_time", "video", {
|
||||
"youtube_id_1_25": "AKqURZnYqpk",
|
||||
"display_name": "default",
|
||||
"youtube_id_1_0": "OEoXaMPEzfM",
|
||||
"end_time": datetime.timedelta(seconds=10),
|
||||
"youtube_id_1_5": "DYpADpL7jAY",
|
||||
"youtube_id_0_75": "JMD_ifUUfsU"
|
||||
}, []),
|
||||
BlockInfo(
|
||||
"T1_changemind_poll_foo_2", "poll_question", {
|
||||
"question": "<p>Have you changed your mind?</p>",
|
||||
"answers": [{"text": "Yes", "id": "yes"}, {"text": "No", "id": "no"}],
|
||||
"xml_attributes": {"reset": "false", "filename": [ "", None]},
|
||||
"display_name": "Change your answer"
|
||||
}, []),
|
||||
]),
|
||||
BlockInfo("unicode", "html", {
|
||||
"data": "…", "xml_attributes": {"filename": ["", None]}
|
||||
}, [])
|
||||
]),
|
||||
]
|
||||
),
|
||||
BlockInfo(
|
||||
"handout_container", "chapter", {
|
||||
"xml_attributes" : {"filename" : ["chapter/handout_container.xml", "chapter/handout_container.xml"]}
|
||||
}, [
|
||||
BlockInfo(
|
||||
"html_7e5578f25f79", "html", {
|
||||
"data": "<a href=\"/static/handouts/sample_handout.txt\"> handouts</a>",
|
||||
"xml_attributes": {"filename": ["", None]}
|
||||
}, []
|
||||
),
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
class ModuleStoreTestCase(TestCase):
|
||||
"""
|
||||
@@ -456,32 +274,34 @@ class ModuleStoreTestCase(TestCase):
|
||||
course_loc: the CourseKey for the created course
|
||||
"""
|
||||
with self.store.branch_setting(ModuleStoreEnum.Branch.draft_preferred, None):
|
||||
# TODO use a single transaction (version, inheritance cache, etc) for this whole thing
|
||||
course = self.store.create_course(org, course, run, self.user.id, fields=course_fields)
|
||||
self.course_loc = course.location
|
||||
# with self.store.bulk_write_operations(self.store.make_course_key(org, course, run)):
|
||||
course = self.store.create_course(org, course, run, self.user.id, fields=course_fields)
|
||||
self.course_loc = course.location
|
||||
|
||||
def create_sub_tree(parent_loc, block_info):
|
||||
block = self.store.create_child(
|
||||
self.user.id,
|
||||
# TODO remove version_agnostic() when we impl the single transaction
|
||||
parent_loc.version_agnostic(),
|
||||
block_info.category, block_id=block_info.block_id,
|
||||
fields=block_info.fields,
|
||||
)
|
||||
for tree in block_info.sub_tree:
|
||||
create_sub_tree(block.location, tree)
|
||||
setattr(self, block_info.block_id, block.location.version_agnostic())
|
||||
def create_sub_tree(parent_loc, block_info):
|
||||
block = self.store.create_child(
|
||||
self.user.id,
|
||||
# TODO remove version_agnostic() when we impl the single transaction
|
||||
parent_loc.version_agnostic(),
|
||||
block_info.category, block_id=block_info.block_id,
|
||||
fields=block_info.fields,
|
||||
)
|
||||
for tree in block_info.sub_tree:
|
||||
create_sub_tree(block.location, tree)
|
||||
setattr(self, block_info.block_id, block.location.version_agnostic())
|
||||
|
||||
for tree in block_info_tree:
|
||||
create_sub_tree(self.course_loc, tree)
|
||||
for tree in block_info_tree:
|
||||
create_sub_tree(self.course_loc, tree)
|
||||
|
||||
self.store.publish(self.course_loc, self.user.id)
|
||||
# remove version_agnostic when bulk write works
|
||||
self.store.publish(self.course_loc.version_agnostic(), self.user.id)
|
||||
return self.course_loc.course_key.version_agnostic()
|
||||
|
||||
def create_toy_course(self, org='edX', course='toy', run='2012_Fall'):
|
||||
"""
|
||||
Create an equiavlent to the toy xml course
|
||||
Create an equivalent to the toy xml course
|
||||
"""
|
||||
# with self.store.bulk_write_operations(self.store.make_course_key(org, course, run)):
|
||||
self.toy_loc = self.create_sample_course(
|
||||
org, course, run, TOY_BLOCK_INFO_TREE,
|
||||
{
|
||||
@@ -490,8 +310,8 @@ class ModuleStoreTestCase(TestCase):
|
||||
"display_name" : "Toy Course",
|
||||
"graded" : True,
|
||||
"tabs" : [
|
||||
CoursewareTab(), # {"type" : "courseware", "name" : "Courseware"},
|
||||
CourseInfoTab(), # {"type" : "course_info", "name" : "Course Info"},
|
||||
CoursewareTab(),
|
||||
CourseInfoTab(),
|
||||
StaticTab(name="Syllabus", url_slug="syllabus"),
|
||||
StaticTab(name="Resources", url_slug="resources"),
|
||||
DiscussionTab(),
|
||||
|
||||
188
common/lib/xmodule/xmodule/modulestore/tests/sample_courses.py
Normal file
188
common/lib/xmodule/xmodule/modulestore/tests/sample_courses.py
Normal file
@@ -0,0 +1,188 @@
|
||||
# encoding: utf-8
|
||||
"""
|
||||
The data type and use of it for declaratively creating test courses.
|
||||
"""
|
||||
# used to create course subtrees in ModuleStoreTestCase.create_test_course
|
||||
# adds to self properties w/ the given block_id which hold the UsageKey for easy retrieval.
|
||||
# fields is a dictionary of keys and values. sub_tree is a collection of BlockInfo
|
||||
from collections import namedtuple
|
||||
import datetime
|
||||
BlockInfo = namedtuple('BlockInfo', 'block_id, category, fields, sub_tree')
|
||||
default_block_info_tree = [
|
||||
BlockInfo(
|
||||
'chapter_x', 'chapter', {}, [
|
||||
BlockInfo(
|
||||
'sequential_x1', 'sequential', {}, [
|
||||
BlockInfo(
|
||||
'vertical_x1a', 'vertical', {}, [
|
||||
BlockInfo('problem_x1a_1', 'problem', {}, []),
|
||||
BlockInfo('problem_x1a_2', 'problem', {}, []),
|
||||
BlockInfo('problem_x1a_3', 'problem', {}, []),
|
||||
BlockInfo('html_x1a_1', 'html', {}, []),
|
||||
]
|
||||
)
|
||||
]
|
||||
)
|
||||
]
|
||||
),
|
||||
BlockInfo(
|
||||
'chapter_y', 'chapter', {}, [
|
||||
BlockInfo(
|
||||
'sequential_y1', 'sequential', {}, [
|
||||
BlockInfo(
|
||||
'vertical_y1a', 'vertical', {}, [
|
||||
BlockInfo('problem_y1a_1', 'problem', {}, []),
|
||||
BlockInfo('problem_y1a_2', 'problem', {}, []),
|
||||
BlockInfo('problem_y1a_3', 'problem', {}, []),
|
||||
]
|
||||
)
|
||||
]
|
||||
)
|
||||
]
|
||||
)
|
||||
]
|
||||
# equivalent to toy course in xml
|
||||
TOY_BLOCK_INFO_TREE = [
|
||||
BlockInfo(
|
||||
'Overview', "chapter", {"display_name" : "Overview"}, [
|
||||
BlockInfo(
|
||||
"Toy_Videos", "videosequence", {
|
||||
"xml_attributes": {"filename": ["", None]}, "display_name": "Toy Videos", "format": "Lecture Sequence"
|
||||
}, [
|
||||
BlockInfo(
|
||||
"secret:toylab", "html", {
|
||||
"data": "<b>Lab 2A: Superposition Experiment</b>\n\n<<<<<<< Updated upstream\n<p>Isn't the toy course great?</p>\n\n<p>Let's add some markup that uses non-ascii characters.\nFor example, we should be able to write words like encyclopædia, or foreign words like français.\nLooking beyond latin-1, we should handle math symbols: πr² ≤ ∞.\nAnd it shouldn't matter if we use entities or numeric codes — Ω ≠ π ≡ Ω ≠ π.\n</p>\n=======\n<p>Isn't the toy course great? — ≤</p>\n>>>>>>> Stashed changes\n",
|
||||
"xml_attributes": { "filename" : [ "html/secret/toylab.xml", "html/secret/toylab.xml" ] },
|
||||
"display_name" : "Toy lab"
|
||||
}, []
|
||||
),
|
||||
BlockInfo(
|
||||
"toyjumpto", "html", {
|
||||
"data" : "<a href=\"/jump_to_id/vertical_test\">This is a link to another page and some Chinese 四節比分和七年前</a> <p>Some more Chinese 四節比分和七年前</p>\n",
|
||||
"xml_attributes": { "filename" : [ "html/toyjumpto.xml", "html/toyjumpto.xml" ] }
|
||||
}, []),
|
||||
BlockInfo(
|
||||
"toyhtml", "html", {
|
||||
"data" : "<a href='/static/handouts/sample_handout.txt'>Sample</a>",
|
||||
"xml_attributes" : { "filename" : [ "html/toyhtml.xml", "html/toyhtml.xml" ] }
|
||||
}, []),
|
||||
BlockInfo(
|
||||
"nonportable", "html", {
|
||||
"data": "<a href=\"/static/foo.jpg\">link</a>\n",
|
||||
"xml_attributes" : { "filename" : [ "html/nonportable.xml", "html/nonportable.xml" ] }
|
||||
}, []),
|
||||
BlockInfo(
|
||||
"nonportable_link", "html", {
|
||||
"data": "<a href=\"/jump_to_id/nonportable_link\">link</a>\n\n",
|
||||
"xml_attributes": {"filename": ["html/nonportable_link.xml", "html/nonportable_link.xml"]}
|
||||
}, []),
|
||||
BlockInfo(
|
||||
"badlink", "html", {
|
||||
"data": "<img src=\"/static//file.jpg\" />\n",
|
||||
"xml_attributes" : { "filename" : [ "html/badlink.xml", "html/badlink.xml" ] }
|
||||
}, []),
|
||||
BlockInfo(
|
||||
"with_styling", "html", {
|
||||
"data": "<p style=\"font:italic bold 72px/30px Georgia, serif; color: red; \">Red text here</p>",
|
||||
"xml_attributes": {"filename": ["html/with_styling.xml", "html/with_styling.xml"]}
|
||||
}, []),
|
||||
BlockInfo(
|
||||
"just_img", "html", {
|
||||
"data": "<img src=\"/static/foo_bar.jpg\" />",
|
||||
"xml_attributes": {"filename": [ "html/just_img.xml", "html/just_img.xml" ] }
|
||||
}, []),
|
||||
BlockInfo(
|
||||
"Video_Resources", "video", {
|
||||
"youtube_id_1_0" : "1bK-WdDi6Qw", "display_name" : "Video Resources"
|
||||
}, []),
|
||||
]),
|
||||
BlockInfo(
|
||||
"Welcome", "video", {"data": "", "youtube_id_1_0": "p2Q6BrNhdh8", "display_name": "Welcome"}, []
|
||||
),
|
||||
BlockInfo(
|
||||
"video_123456789012", "video", {"data": "", "youtube_id_1_0": "p2Q6BrNhdh8", "display_name": "Test Video"}, []
|
||||
),
|
||||
BlockInfo(
|
||||
"video_4f66f493ac8f", "video", {"youtube_id_1_0": "p2Q6BrNhdh8"}, []
|
||||
)
|
||||
]
|
||||
),
|
||||
BlockInfo(
|
||||
"secret:magic", "chapter", {
|
||||
"xml_attributes": {"filename": [ "chapter/secret/magic.xml", "chapter/secret/magic.xml"]}
|
||||
}, [
|
||||
BlockInfo(
|
||||
"toyvideo", "video", {"youtube_id_1_0": "OEoXaMPEzfMA", "display_name": "toyvideo"}, []
|
||||
)
|
||||
]
|
||||
),
|
||||
BlockInfo(
|
||||
"poll_test", "chapter", {}, [
|
||||
BlockInfo(
|
||||
"T1_changemind_poll_foo", "poll_question", {
|
||||
"question": "<p>Have you changed your mind? ’</p>",
|
||||
"answers": [{"text": "Yes", "id": "yes"}, {"text": "No", "id": "no"}],
|
||||
"xml_attributes": {"reset": "false", "filename": ["", None]},
|
||||
"display_name": "Change your answer"
|
||||
}, []) ]
|
||||
),
|
||||
BlockInfo(
|
||||
"vertical_container", "chapter", {
|
||||
"xml_attributes": {"filename": ["chapter/vertical_container.xml", "chapter/vertical_container.xml"]}
|
||||
}, [
|
||||
BlockInfo("vertical_sequential", "sequential", {}, [
|
||||
BlockInfo("vertical_test", "vertical", {
|
||||
"xml_attributes": {"filename": ["vertical/vertical_test.xml", "vertical_test"]}
|
||||
}, [
|
||||
BlockInfo(
|
||||
"sample_video", "video", {
|
||||
"youtube_id_1_25": "AKqURZnYqpk",
|
||||
"youtube_id_0_75": "JMD_ifUUfsU",
|
||||
"youtube_id_1_0": "OEoXaMPEzfM",
|
||||
"display_name": "default",
|
||||
"youtube_id_1_5": "DYpADpL7jAY"
|
||||
}, []),
|
||||
BlockInfo(
|
||||
"separate_file_video", "video", {
|
||||
"youtube_id_1_25": "AKqURZnYqpk",
|
||||
"youtube_id_0_75": "JMD_ifUUfsU",
|
||||
"youtube_id_1_0": "OEoXaMPEzfM",
|
||||
"display_name": "default",
|
||||
"youtube_id_1_5": "DYpADpL7jAY"
|
||||
}, []),
|
||||
BlockInfo(
|
||||
"video_with_end_time", "video", {
|
||||
"youtube_id_1_25": "AKqURZnYqpk",
|
||||
"display_name": "default",
|
||||
"youtube_id_1_0": "OEoXaMPEzfM",
|
||||
"end_time": datetime.timedelta(seconds=10),
|
||||
"youtube_id_1_5": "DYpADpL7jAY",
|
||||
"youtube_id_0_75": "JMD_ifUUfsU"
|
||||
}, []),
|
||||
BlockInfo(
|
||||
"T1_changemind_poll_foo_2", "poll_question", {
|
||||
"question": "<p>Have you changed your mind?</p>",
|
||||
"answers": [{"text": "Yes", "id": "yes"}, {"text": "No", "id": "no"}],
|
||||
"xml_attributes": {"reset": "false", "filename": [ "", None]},
|
||||
"display_name": "Change your answer"
|
||||
}, []),
|
||||
]),
|
||||
BlockInfo("unicode", "html", {
|
||||
"data": "…", "xml_attributes": {"filename": ["", None]}
|
||||
}, [])
|
||||
]),
|
||||
]
|
||||
),
|
||||
BlockInfo(
|
||||
"handout_container", "chapter", {
|
||||
"xml_attributes" : {"filename" : ["chapter/handout_container.xml", "chapter/handout_container.xml"]}
|
||||
}, [
|
||||
BlockInfo(
|
||||
"html_7e5578f25f79", "html", {
|
||||
"data": "<a href=\"/static/handouts/sample_handout.txt\"> handouts</a>",
|
||||
"xml_attributes": {"filename": ["", None]}
|
||||
}, []
|
||||
),
|
||||
]
|
||||
)
|
||||
]
|
||||
Reference in New Issue
Block a user