Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
d2a0df41
Commit
d2a0df41
authored
Jan 14, 2014
by
Calen Pennington
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #2129 from cpennington/xblocks-xml-import-export
XBlock Xml Serialization/Deserialization
parents
3c3ef50e
a55724d1
Show whitespace changes
Inline
Side-by-side
Showing
39 changed files
with
661 additions
and
339 deletions
+661
-339
cms/djangoapps/contentstore/tests/test_contentstore.py
+1
-1
cms/djangoapps/contentstore/views/preview.py
+2
-2
common/lib/xmodule/xmodule/abtest_module.py
+2
-1
common/lib/xmodule/xmodule/backcompat_module.py
+4
-4
common/lib/xmodule/xmodule/conditional_module.py
+5
-6
common/lib/xmodule/xmodule/course_module.py
+2
-2
common/lib/xmodule/xmodule/crowdsource_hinter.py
+2
-1
common/lib/xmodule/xmodule/error_module.py
+4
-6
common/lib/xmodule/xmodule/modulestore/__init__.py
+30
-9
common/lib/xmodule/xmodule/modulestore/inheritance.py
+57
-12
common/lib/xmodule/xmodule/modulestore/locator.py
+2
-1
common/lib/xmodule/xmodule/modulestore/mongo/base.py
+16
-7
common/lib/xmodule/xmodule/modulestore/split_mongo/caching_descriptor_system.py
+25
-3
common/lib/xmodule/xmodule/modulestore/split_mongo/split.py
+3
-3
common/lib/xmodule/xmodule/modulestore/tests/test_location.py
+121
-103
common/lib/xmodule/xmodule/modulestore/tests/test_mongo.py
+0
-7
common/lib/xmodule/xmodule/modulestore/xml.py
+101
-39
common/lib/xmodule/xmodule/modulestore/xml_importer.py
+2
-4
common/lib/xmodule/xmodule/peer_grading_module.py
+16
-15
common/lib/xmodule/xmodule/seq_module.py
+2
-1
common/lib/xmodule/xmodule/tests/__init__.py
+4
-0
common/lib/xmodule/xmodule/tests/test_conditional.py
+2
-4
common/lib/xmodule/xmodule/tests/test_course_module.py
+5
-3
common/lib/xmodule/xmodule/tests/test_error_module.py
+17
-6
common/lib/xmodule/xmodule/tests/test_import.py
+47
-11
common/lib/xmodule/xmodule/tests/test_peer_grading.py
+1
-1
common/lib/xmodule/xmodule/tests/test_video.py
+10
-9
common/lib/xmodule/xmodule/tests/test_xblock_wrappers.py
+4
-2
common/lib/xmodule/xmodule/tests/test_xml_module.py
+85
-4
common/lib/xmodule/xmodule/tests/xml/__init__.py
+20
-6
common/lib/xmodule/xmodule/video_module.py
+9
-10
common/lib/xmodule/xmodule/x_module.py
+34
-24
common/lib/xmodule/xmodule/xml_module.py
+16
-24
common/test/data/simple/course.xml
+0
-1
lms/djangoapps/courseware/management/commands/dump_course_structure.py
+2
-2
lms/djangoapps/courseware/model_data.py
+1
-1
lms/djangoapps/courseware/module_render.py
+2
-2
requirements/edx/base.txt
+0
-1
requirements/edx/github.txt
+5
-1
No files found.
cms/djangoapps/contentstore/tests/test_contentstore.py
View file @
d2a0df41
...
@@ -1501,7 +1501,7 @@ class ContentStoreTest(ModuleStoreTestCase):
...
@@ -1501,7 +1501,7 @@ class ContentStoreTest(ModuleStoreTestCase):
"""Test new course creation - error path for bad organization name"""
"""Test new course creation - error path for bad organization name"""
self
.
course_data
[
'org'
]
=
'University of California, Berkeley'
self
.
course_data
[
'org'
]
=
'University of California, Berkeley'
self
.
assert_course_creation_failed
(
self
.
assert_course_creation_failed
(
"Unable to create course 'Robot Super Course'.
\n\n
Invalid characters in 'University of California, Berkeley'."
)
"Unable to create course 'Robot Super Course'.
\n\n
Invalid characters in
u
'University of California, Berkeley'."
)
def
test_create_course_with_course_creation_disabled_staff
(
self
):
def
test_create_course_with_course_creation_disabled_staff
(
self
):
"""Test new course creation -- course creation disabled, but staff access."""
"""Test new course creation -- course creation disabled, but staff access."""
...
...
cms/djangoapps/contentstore/views/preview.py
View file @
d2a0df41
...
@@ -12,7 +12,7 @@ from xmodule.error_module import ErrorDescriptor
...
@@ -12,7 +12,7 @@ from xmodule.error_module import ErrorDescriptor
from
xmodule.exceptions
import
NotFoundError
,
ProcessingError
from
xmodule.exceptions
import
NotFoundError
,
ProcessingError
from
xmodule.modulestore.django
import
modulestore
from
xmodule.modulestore.django
import
modulestore
from
xmodule.x_module
import
ModuleSystem
from
xmodule.x_module
import
ModuleSystem
from
xblock.runtime
import
DbModel
from
xblock.runtime
import
KvsFieldData
from
xblock.django.request
import
webob_to_django_response
,
django_to_webob_request
from
xblock.django.request
import
webob_to_django_response
,
django_to_webob_request
from
xblock.exceptions
import
NoSuchHandlerError
from
xblock.exceptions
import
NoSuchHandlerError
...
@@ -142,7 +142,7 @@ def _load_preview_module(request, descriptor):
...
@@ -142,7 +142,7 @@ def _load_preview_module(request, descriptor):
request: The active django request
request: The active django request
descriptor: An XModuleDescriptor
descriptor: An XModuleDescriptor
"""
"""
student_data
=
DbModel
(
SessionKeyValueStore
(
request
))
student_data
=
KvsFieldData
(
SessionKeyValueStore
(
request
))
descriptor
.
bind_for_student
(
descriptor
.
bind_for_student
(
_preview_module_system
(
request
,
descriptor
),
_preview_module_system
(
request
,
descriptor
),
LmsFieldData
(
descriptor
.
_field_data
,
student_data
),
# pylint: disable=protected-access
LmsFieldData
(
descriptor
.
_field_data
,
student_data
),
# pylint: disable=protected-access
...
...
common/lib/xmodule/xmodule/abtest_module.py
View file @
d2a0df41
...
@@ -111,7 +111,8 @@ class ABTestDescriptor(ABTestFields, RawDescriptor, XmlDescriptor):
...
@@ -111,7 +111,8 @@ class ABTestDescriptor(ABTestFields, RawDescriptor, XmlDescriptor):
child_content_urls
=
[]
child_content_urls
=
[]
for
child
in
group
:
for
child
in
group
:
try
:
try
:
child_content_urls
.
append
(
system
.
process_xml
(
etree
.
tostring
(
child
))
.
location
.
url
())
child_block
=
system
.
process_xml
(
etree
.
tostring
(
child
))
child_content_urls
.
append
(
child_block
.
scope_ids
.
usage_id
)
except
:
except
:
log
.
exception
(
"Unable to load child when parsing ABTest. Continuing..."
)
log
.
exception
(
"Unable to load child when parsing ABTest. Continuing..."
)
continue
continue
...
...
common/lib/xmodule/xmodule/backcompat_module.py
View file @
d2a0df41
...
@@ -17,7 +17,7 @@ def process_includes(fn):
...
@@ -17,7 +17,7 @@ def process_includes(fn):
are supposed to include
are supposed to include
"""
"""
@wraps
(
fn
)
@wraps
(
fn
)
def
from_xml
(
cls
,
xml_data
,
system
,
org
=
None
,
course
=
None
):
def
from_xml
(
cls
,
xml_data
,
system
,
id_generator
):
xml_object
=
etree
.
fromstring
(
xml_data
)
xml_object
=
etree
.
fromstring
(
xml_data
)
next_include
=
xml_object
.
find
(
'include'
)
next_include
=
xml_object
.
find
(
'include'
)
while
next_include
is
not
None
:
while
next_include
is
not
None
:
...
@@ -55,14 +55,14 @@ def process_includes(fn):
...
@@ -55,14 +55,14 @@ def process_includes(fn):
parent
.
remove
(
next_include
)
parent
.
remove
(
next_include
)
next_include
=
xml_object
.
find
(
'include'
)
next_include
=
xml_object
.
find
(
'include'
)
return
fn
(
cls
,
etree
.
tostring
(
xml_object
),
system
,
org
,
course
)
return
fn
(
cls
,
etree
.
tostring
(
xml_object
),
system
,
id_generator
)
return
from_xml
return
from_xml
class
SemanticSectionDescriptor
(
XModuleDescriptor
):
class
SemanticSectionDescriptor
(
XModuleDescriptor
):
@classmethod
@classmethod
@process_includes
@process_includes
def
from_xml
(
cls
,
xml_data
,
system
,
org
=
None
,
course
=
None
):
def
from_xml
(
cls
,
xml_data
,
system
,
id_generator
):
"""
"""
Removes sections with single child elements in favor of just embedding
Removes sections with single child elements in favor of just embedding
the child element
the child element
...
@@ -83,7 +83,7 @@ class SemanticSectionDescriptor(XModuleDescriptor):
...
@@ -83,7 +83,7 @@ class SemanticSectionDescriptor(XModuleDescriptor):
class
TranslateCustomTagDescriptor
(
XModuleDescriptor
):
class
TranslateCustomTagDescriptor
(
XModuleDescriptor
):
@classmethod
@classmethod
def
from_xml
(
cls
,
xml_data
,
system
,
org
=
None
,
course
=
None
):
def
from_xml
(
cls
,
xml_data
,
system
,
id_generator
):
"""
"""
Transforms the xml_data from <$custom_tag attr="" attr=""/> to
Transforms the xml_data from <$custom_tag attr="" attr=""/> to
<customtag attr="" attr="" impl="$custom_tag"/>
<customtag attr="" attr="" impl="$custom_tag"/>
...
...
common/lib/xmodule/xmodule/conditional_module.py
View file @
d2a0df41
...
@@ -20,7 +20,7 @@ log = logging.getLogger('edx.' + __name__)
...
@@ -20,7 +20,7 @@ log = logging.getLogger('edx.' + __name__)
class
ConditionalFields
(
object
):
class
ConditionalFields
(
object
):
has_children
=
True
has_children
=
True
show_tag_list
=
List
(
help
=
"
Poll answer
s"
,
scope
=
Scope
.
content
)
show_tag_list
=
List
(
help
=
"
List of urls of children that are references to external module
s"
,
scope
=
Scope
.
content
)
class
ConditionalModule
(
ConditionalFields
,
XModule
):
class
ConditionalModule
(
ConditionalFields
,
XModule
):
...
@@ -196,6 +196,7 @@ class ConditionalDescriptor(ConditionalFields, SequenceDescriptor):
...
@@ -196,6 +196,7 @@ class ConditionalDescriptor(ConditionalFields, SequenceDescriptor):
locations
=
[
location
.
strip
()
for
location
in
sources
.
split
(
';'
)]
locations
=
[
location
.
strip
()
for
location
in
sources
.
split
(
';'
)]
for
location
in
locations
:
for
location
in
locations
:
if
Location
.
is_valid
(
location
):
# Check valid location url.
if
Location
.
is_valid
(
location
):
# Check valid location url.
location
=
Location
(
location
)
try
:
try
:
if
return_descriptor
:
if
return_descriptor
:
descriptor
=
system
.
load_item
(
location
)
descriptor
=
system
.
load_item
(
location
)
...
@@ -221,15 +222,13 @@ class ConditionalDescriptor(ConditionalFields, SequenceDescriptor):
...
@@ -221,15 +222,13 @@ class ConditionalDescriptor(ConditionalFields, SequenceDescriptor):
show_tag_list
=
[]
show_tag_list
=
[]
for
child
in
xml_object
:
for
child
in
xml_object
:
if
child
.
tag
==
'show'
:
if
child
.
tag
==
'show'
:
location
=
ConditionalDescriptor
.
parse_sources
(
location
=
ConditionalDescriptor
.
parse_sources
(
child
,
system
)
child
,
system
)
children
.
extend
(
location
)
children
.
extend
(
location
)
show_tag_list
.
extend
(
location
)
show_tag_list
.
extend
(
location
.
url
()
)
else
:
else
:
try
:
try
:
descriptor
=
system
.
process_xml
(
etree
.
tostring
(
child
))
descriptor
=
system
.
process_xml
(
etree
.
tostring
(
child
))
module_url
=
descriptor
.
location
.
url
()
children
.
append
(
descriptor
.
scope_ids
.
usage_id
)
children
.
append
(
module_url
)
except
:
except
:
msg
=
"Unable to load child when parsing Conditional."
msg
=
"Unable to load child when parsing Conditional."
log
.
exception
(
msg
)
log
.
exception
(
msg
)
...
...
common/lib/xmodule/xmodule/course_module.py
View file @
d2a0df41
...
@@ -497,8 +497,8 @@ class CourseDescriptor(CourseFields, SequenceDescriptor):
...
@@ -497,8 +497,8 @@ class CourseDescriptor(CourseFields, SequenceDescriptor):
return
policy_str
return
policy_str
@classmethod
@classmethod
def
from_xml
(
cls
,
xml_data
,
system
,
org
=
None
,
course
=
None
):
def
from_xml
(
cls
,
xml_data
,
system
,
id_generator
):
instance
=
super
(
CourseDescriptor
,
cls
)
.
from_xml
(
xml_data
,
system
,
org
,
course
)
instance
=
super
(
CourseDescriptor
,
cls
)
.
from_xml
(
xml_data
,
system
,
id_generator
)
# bleh, have to parse the XML here to just pull out the url_name attribute
# bleh, have to parse the XML here to just pull out the url_name attribute
# I don't think it's stored anywhere in the instance.
# I don't think it's stored anywhere in the instance.
...
...
common/lib/xmodule/xmodule/crowdsource_hinter.py
View file @
d2a0df41
...
@@ -388,7 +388,8 @@ class CrowdsourceHinterDescriptor(CrowdsourceHinterFields, RawDescriptor):
...
@@ -388,7 +388,8 @@ class CrowdsourceHinterDescriptor(CrowdsourceHinterFields, RawDescriptor):
children
=
[]
children
=
[]
for
child
in
xml_object
:
for
child
in
xml_object
:
try
:
try
:
children
.
append
(
system
.
process_xml
(
etree
.
tostring
(
child
,
encoding
=
'unicode'
))
.
location
.
url
())
child_block
=
system
.
process_xml
(
etree
.
tostring
(
child
,
encoding
=
'unicode'
))
children
.
append
(
child_block
.
scope_ids
.
usage_id
)
except
Exception
as
e
:
except
Exception
as
e
:
log
.
exception
(
"Unable to load child when parsing CrowdsourceHinter. Continuing..."
)
log
.
exception
(
"Unable to load child when parsing CrowdsourceHinter. Continuing..."
)
if
system
.
error_tracker
is
not
None
:
if
system
.
error_tracker
is
not
None
:
...
...
common/lib/xmodule/xmodule/error_module.py
View file @
d2a0df41
...
@@ -81,12 +81,10 @@ class ErrorDescriptor(ErrorFields, XModuleDescriptor):
...
@@ -81,12 +81,10 @@ class ErrorDescriptor(ErrorFields, XModuleDescriptor):
@classmethod
@classmethod
def
_construct
(
cls
,
system
,
contents
,
error_msg
,
location
):
def
_construct
(
cls
,
system
,
contents
,
error_msg
,
location
):
if
isinstance
(
location
,
dict
)
and
'course'
in
location
:
location
=
Location
(
location
)
location
=
Location
(
location
)
if
isinstance
(
location
,
Location
)
and
location
.
name
is
None
:
if
location
.
category
==
'error'
:
location
=
location
.
replace
(
location
=
location
.
replace
(
category
=
'error'
,
# Pick a unique url_name -- the sha1 hash of the contents.
# Pick a unique url_name -- the sha1 hash of the contents.
# NOTE: We could try to pull out the url_name of the errored descriptor,
# NOTE: We could try to pull out the url_name of the errored descriptor,
# but url_names aren't guaranteed to be unique between descriptor types,
# but url_names aren't guaranteed to be unique between descriptor types,
...
@@ -136,7 +134,7 @@ class ErrorDescriptor(ErrorFields, XModuleDescriptor):
...
@@ -136,7 +134,7 @@ class ErrorDescriptor(ErrorFields, XModuleDescriptor):
)
)
@classmethod
@classmethod
def
from_xml
(
cls
,
xml_data
,
system
,
org
=
None
,
course
=
None
,
def
from_xml
(
cls
,
xml_data
,
system
,
id_generator
,
error_msg
=
'Error not available'
):
error_msg
=
'Error not available'
):
'''Create an instance of this descriptor from the supplied data.
'''Create an instance of this descriptor from the supplied data.
...
@@ -162,7 +160,7 @@ class ErrorDescriptor(ErrorFields, XModuleDescriptor):
...
@@ -162,7 +160,7 @@ class ErrorDescriptor(ErrorFields, XModuleDescriptor):
# Save the error to display later--overrides other problems
# Save the error to display later--overrides other problems
error_msg
=
exc_info_to_str
(
sys
.
exc_info
())
error_msg
=
exc_info_to_str
(
sys
.
exc_info
())
return
cls
.
_construct
(
system
,
xml_data
,
error_msg
,
location
=
Location
(
'i4x'
,
org
,
course
,
None
,
None
))
return
cls
.
_construct
(
system
,
xml_data
,
error_msg
,
location
=
id_generator
.
create_definition
(
'error'
))
def
export_to_xml
(
self
,
resource_fs
):
def
export_to_xml
(
self
,
resource_fs
):
'''
'''
...
...
common/lib/xmodule/xmodule/modulestore/__init__.py
View file @
d2a0df41
...
@@ -40,6 +40,21 @@ INVALID_HTML_CHARS = re.compile(r"[^\w-]")
...
@@ -40,6 +40,21 @@ INVALID_HTML_CHARS = re.compile(r"[^\w-]")
_LocationBase
=
namedtuple
(
'LocationBase'
,
'tag org course category name revision'
)
_LocationBase
=
namedtuple
(
'LocationBase'
,
'tag org course category name revision'
)
def
_check_location_part
(
val
,
regexp
):
"""
Check that `regexp` doesn't match inside `val`. If it does, raise an exception
Args:
val (string): The value to check
regexp (re.RegexObject): The regular expression specifying invalid characters
Raises:
InvalidLocationError: Raised if any invalid character is found in `val`
"""
if
val
is
not
None
and
regexp
.
search
(
val
)
is
not
None
:
raise
InvalidLocationError
(
"Invalid characters in {!r}."
.
format
(
val
))
class
Location
(
_LocationBase
):
class
Location
(
_LocationBase
):
'''
'''
Encodes a location.
Encodes a location.
...
@@ -145,7 +160,6 @@ class Location(_LocationBase):
...
@@ -145,7 +160,6 @@ class Location(_LocationBase):
Components may be set to None, which may be interpreted in some contexts
Components may be set to None, which may be interpreted in some contexts
to mean wildcard selection.
to mean wildcard selection.
"""
"""
if
(
org
is
None
and
course
is
None
and
category
is
None
and
name
is
None
and
revision
is
None
):
if
(
org
is
None
and
course
is
None
and
category
is
None
and
name
is
None
and
revision
is
None
):
location
=
loc_or_tag
location
=
loc_or_tag
else
:
else
:
...
@@ -161,23 +175,18 @@ class Location(_LocationBase):
...
@@ -161,23 +175,18 @@ class Location(_LocationBase):
check_list
(
list_
)
check_list
(
list_
)
def
check_list
(
list_
):
def
check_list
(
list_
):
def
check
(
val
,
regexp
):
if
val
is
not
None
and
regexp
.
search
(
val
)
is
not
None
:
log
.
debug
(
'invalid characters val="
%
s", list_="
%
s"'
%
(
val
,
list_
))
raise
InvalidLocationError
(
"Invalid characters in '
%
s'."
%
(
val
))
list_
=
list
(
list_
)
list_
=
list
(
list_
)
for
val
in
list_
[:
4
]
+
[
list_
[
5
]]:
for
val
in
list_
[:
4
]
+
[
list_
[
5
]]:
check
(
val
,
INVALID_CHARS
)
_check_location_part
(
val
,
INVALID_CHARS
)
# names allow colons
# names allow colons
check
(
list_
[
4
],
INVALID_CHARS_NAME
)
_check_location_part
(
list_
[
4
],
INVALID_CHARS_NAME
)
if
isinstance
(
location
,
Location
):
if
isinstance
(
location
,
Location
):
return
location
return
location
elif
isinstance
(
location
,
basestring
):
elif
isinstance
(
location
,
basestring
):
match
=
URL_RE
.
match
(
location
)
match
=
URL_RE
.
match
(
location
)
if
match
is
None
:
if
match
is
None
:
log
.
debug
(
'location is instance of
%
s but no URL match'
%
basestring
)
log
.
debug
(
"location
%
r doesn't match URL"
,
location
)
raise
InvalidLocationError
(
location
)
raise
InvalidLocationError
(
location
)
groups
=
match
.
groupdict
()
groups
=
match
.
groupdict
()
check_dict
(
groups
)
check_dict
(
groups
)
...
@@ -249,6 +258,18 @@ class Location(_LocationBase):
...
@@ -249,6 +258,18 @@ class Location(_LocationBase):
return
"/"
.
join
([
self
.
org
,
self
.
course
,
self
.
name
])
return
"/"
.
join
([
self
.
org
,
self
.
course
,
self
.
name
])
def
_replace
(
self
,
**
kwargs
):
"""
Return a new :class:`Location` with values replaced
by the values specified in `**kwargs`
"""
for
name
,
value
in
kwargs
.
iteritems
():
if
name
==
'name'
:
_check_location_part
(
value
,
INVALID_CHARS_NAME
)
else
:
_check_location_part
(
value
,
INVALID_CHARS
)
return
super
(
Location
,
self
)
.
_replace
(
**
kwargs
)
def
replace
(
self
,
**
kwargs
):
def
replace
(
self
,
**
kwargs
):
'''
'''
Expose a public method for replacing location elements
Expose a public method for replacing location elements
...
...
common/lib/xmodule/xmodule/modulestore/inheritance.py
View file @
d2a0df41
"""
Support for inheritance of fields down an XBlock hierarchy.
"""
from
datetime
import
datetime
from
datetime
import
datetime
from
pytz
import
UTC
from
pytz
import
UTC
from
xblock.fields
import
Scope
,
Boolean
,
String
,
Float
,
XBlockMixin
,
Dict
from
xblock.fields
import
Scope
,
Boolean
,
String
,
Float
,
XBlockMixin
,
Dict
from
xblock.runtime
import
KeyValueStore
,
KvsFieldData
from
xmodule.fields
import
Date
,
Timedelta
from
xmodule.fields
import
Date
,
Timedelta
from
xblock.runtime
import
KeyValueStore
class
InheritanceMixin
(
XBlockMixin
):
class
InheritanceMixin
(
XBlockMixin
):
"""Field definitions for inheritable fields"""
"""Field definitions for inheritable fields
.
"""
graded
=
Boolean
(
graded
=
Boolean
(
help
=
"Whether this module contributes to the final course grade"
,
help
=
"Whether this module contributes to the final course grade"
,
scope
=
Scope
.
settings
,
default
=
False
,
default
=
False
,
scope
=
Scope
.
settings
)
)
start
=
Date
(
start
=
Date
(
help
=
"Start time when this module is visible"
,
help
=
"Start time when this module is visible"
,
default
=
datetime
(
2030
,
1
,
1
,
tzinfo
=
UTC
),
default
=
datetime
(
2030
,
1
,
1
,
tzinfo
=
UTC
),
scope
=
Scope
.
settings
scope
=
Scope
.
settings
)
)
due
=
Date
(
help
=
"Date that this problem is due by"
,
scope
=
Scope
.
settings
)
due
=
Date
(
help
=
"Date that this problem is due by"
,
scope
=
Scope
.
settings
,
)
extended_due
=
Date
(
extended_due
=
Date
(
help
=
"Date that this problem is due by for a particular student. This "
help
=
"Date that this problem is due by for a particular student. This "
"can be set by an instructor, and will override the global due "
"can be set by an instructor, and will override the global due "
...
@@ -29,31 +36,38 @@ class InheritanceMixin(XBlockMixin):
...
@@ -29,31 +36,38 @@ class InheritanceMixin(XBlockMixin):
default
=
None
,
default
=
None
,
scope
=
Scope
.
user_state
,
scope
=
Scope
.
user_state
,
)
)
giturl
=
String
(
help
=
"url root for course data git repository"
,
scope
=
Scope
.
settings
)
giturl
=
String
(
help
=
"url root for course data git repository"
,
scope
=
Scope
.
settings
,
)
xqa_key
=
String
(
help
=
"DO NOT USE"
,
scope
=
Scope
.
settings
)
xqa_key
=
String
(
help
=
"DO NOT USE"
,
scope
=
Scope
.
settings
)
graceperiod
=
Timedelta
(
graceperiod
=
Timedelta
(
help
=
"Amount of time after the due date that submissions will be accepted"
,
help
=
"Amount of time after the due date that submissions will be accepted"
,
scope
=
Scope
.
settings
scope
=
Scope
.
settings
,
)
)
showanswer
=
String
(
showanswer
=
String
(
help
=
"When to show the problem answer to the student"
,
help
=
"When to show the problem answer to the student"
,
scope
=
Scope
.
settings
,
scope
=
Scope
.
settings
,
default
=
"finished"
default
=
"finished"
,
)
)
rerandomize
=
String
(
rerandomize
=
String
(
help
=
"When to rerandomize the problem"
,
help
=
"When to rerandomize the problem"
,
scope
=
Scope
.
settings
,
default
=
"never"
,
default
=
"never"
,
scope
=
Scope
.
settings
)
)
days_early_for_beta
=
Float
(
days_early_for_beta
=
Float
(
help
=
"Number of days early to show content to beta users"
,
help
=
"Number of days early to show content to beta users"
,
scope
=
Scope
.
settings
,
default
=
None
,
default
=
None
,
scope
=
Scope
.
settings
)
)
static_asset_path
=
String
(
help
=
"Path to use for static assets - overrides Studio c4x://"
,
scope
=
Scope
.
settings
,
default
=
''
)
static_asset_path
=
String
(
help
=
"Path to use for static assets - overrides Studio c4x://"
,
scope
=
Scope
.
settings
,
default
=
''
,
)
text_customization
=
Dict
(
text_customization
=
Dict
(
help
=
"String customization substitutions for particular locations"
,
help
=
"String customization substitutions for particular locations"
,
scope
=
Scope
.
settings
scope
=
Scope
.
settings
,
)
)
use_latex_compiler
=
Boolean
(
use_latex_compiler
=
Boolean
(
help
=
"Enable LaTeX templates?"
,
help
=
"Enable LaTeX templates?"
,
...
@@ -104,6 +118,37 @@ def own_metadata(module):
...
@@ -104,6 +118,37 @@ def own_metadata(module):
return
module
.
get_explicitly_set_fields_by_scope
(
Scope
.
settings
)
return
module
.
get_explicitly_set_fields_by_scope
(
Scope
.
settings
)
class
InheritingFieldData
(
KvsFieldData
):
"""A `FieldData` implementation that can inherit value from parents to children."""
def
__init__
(
self
,
inheritable_names
,
**
kwargs
):
"""
`inheritable_names` is a list of names that can be inherited from
parents.
"""
super
(
InheritingFieldData
,
self
)
.
__init__
(
**
kwargs
)
self
.
inheritable_names
=
set
(
inheritable_names
)
def
default
(
self
,
block
,
name
):
"""
The default for an inheritable name is found on a parent.
"""
if
name
in
self
.
inheritable_names
and
block
.
parent
is
not
None
:
parent
=
block
.
get_parent
()
if
parent
:
return
getattr
(
parent
,
name
)
super
(
InheritingFieldData
,
self
)
.
default
(
block
,
name
)
def
inheriting_field_data
(
kvs
):
"""Create an InheritanceFieldData that inherits the names in InheritanceMixin."""
return
InheritingFieldData
(
inheritable_names
=
InheritanceMixin
.
fields
.
keys
(),
kvs
=
kvs
,
)
class
InheritanceKeyValueStore
(
KeyValueStore
):
class
InheritanceKeyValueStore
(
KeyValueStore
):
"""
"""
Common superclass for kvs's which know about inheritance of settings. Offers simple
Common superclass for kvs's which know about inheritance of settings. Offers simple
...
...
common/lib/xmodule/xmodule/modulestore/locator.py
View file @
d2a0df41
...
@@ -26,7 +26,8 @@ class LocalId(object):
...
@@ -26,7 +26,8 @@ class LocalId(object):
Should be hashable and distinguishable, but nothing else
Should be hashable and distinguishable, but nothing else
"""
"""
pass
def
__str__
(
self
):
return
"localid_{}"
.
format
(
id
(
self
))
class
Locator
(
object
):
class
Locator
(
object
):
...
...
common/lib/xmodule/xmodule/modulestore/mongo/base.py
View file @
d2a0df41
...
@@ -26,13 +26,14 @@ from importlib import import_module
...
@@ -26,13 +26,14 @@ from importlib import import_module
from
xmodule.errortracker
import
null_error_tracker
,
exc_info_to_str
from
xmodule.errortracker
import
null_error_tracker
,
exc_info_to_str
from
xmodule.mako_module
import
MakoDescriptorSystem
from
xmodule.mako_module
import
MakoDescriptorSystem
from
xmodule.error_module
import
ErrorDescriptor
from
xmodule.error_module
import
ErrorDescriptor
from
xblock.runtime
import
DbModel
from
xblock.runtime
import
KvsFieldData
from
xblock.exceptions
import
InvalidScopeError
from
xblock.exceptions
import
InvalidScopeError
from
xblock.fields
import
Scope
,
ScopeIds
from
xblock.fields
import
Scope
,
ScopeIds
from
xmodule.modulestore
import
ModuleStoreWriteBase
,
Location
,
MONGO_MODULESTORE_TYPE
from
xmodule.modulestore
import
ModuleStoreWriteBase
,
Location
,
MONGO_MODULESTORE_TYPE
from
xmodule.modulestore.exceptions
import
ItemNotFoundError
from
xmodule.modulestore.exceptions
import
ItemNotFoundError
from
xmodule.modulestore.inheritance
import
own_metadata
,
InheritanceMixin
,
inherit_metadata
,
InheritanceKeyValueStore
from
xmodule.modulestore.inheritance
import
own_metadata
,
InheritanceMixin
,
inherit_metadata
,
InheritanceKeyValueStore
from
xmodule.modulestore.xml
import
LocationReader
log
=
logging
.
getLogger
(
__name__
)
log
=
logging
.
getLogger
(
__name__
)
...
@@ -146,7 +147,12 @@ class CachingDescriptorSystem(MakoDescriptorSystem):
...
@@ -146,7 +147,12 @@ class CachingDescriptorSystem(MakoDescriptorSystem):
render_template: a function for rendering templates, as per
render_template: a function for rendering templates, as per
MakoDescriptorSystem
MakoDescriptorSystem
"""
"""
super
(
CachingDescriptorSystem
,
self
)
.
__init__
(
load_item
=
self
.
load_item
,
**
kwargs
)
super
(
CachingDescriptorSystem
,
self
)
.
__init__
(
id_reader
=
LocationReader
(),
field_data
=
None
,
load_item
=
self
.
load_item
,
**
kwargs
)
self
.
modulestore
=
modulestore
self
.
modulestore
=
modulestore
self
.
module_data
=
module_data
self
.
module_data
=
module_data
...
@@ -187,7 +193,7 @@ class CachingDescriptorSystem(MakoDescriptorSystem):
...
@@ -187,7 +193,7 @@ class CachingDescriptorSystem(MakoDescriptorSystem):
metadata
,
metadata
,
)
)
field_data
=
DbModel
(
kvs
)
field_data
=
KvsFieldData
(
kvs
)
scope_ids
=
ScopeIds
(
None
,
category
,
location
,
location
)
scope_ids
=
ScopeIds
(
None
,
category
,
location
,
location
)
module
=
self
.
construct_xblock_from_class
(
class_
,
scope_ids
,
field_data
)
module
=
self
.
construct_xblock_from_class
(
class_
,
scope_ids
,
field_data
)
if
self
.
cached_metadata
is
not
None
:
if
self
.
cached_metadata
is
not
None
:
...
@@ -480,7 +486,8 @@ class MongoModuleStore(ModuleStoreWriteBase):
...
@@ -480,7 +486,8 @@ class MongoModuleStore(ModuleStoreWriteBase):
"""
"""
Load an XModuleDescriptor from item, using the children stored in data_cache
Load an XModuleDescriptor from item, using the children stored in data_cache
"""
"""
data_dir
=
getattr
(
item
,
'data_dir'
,
item
[
'location'
][
'course'
])
location
=
Location
(
item
[
'location'
])
data_dir
=
getattr
(
item
,
'data_dir'
,
location
.
course
)
root
=
self
.
fs_root
/
data_dir
root
=
self
.
fs_root
/
data_dir
if
not
root
.
isdir
():
if
not
root
.
isdir
():
...
@@ -490,7 +497,7 @@ class MongoModuleStore(ModuleStoreWriteBase):
...
@@ -490,7 +497,7 @@ class MongoModuleStore(ModuleStoreWriteBase):
cached_metadata
=
{}
cached_metadata
=
{}
if
apply_cached_metadata
:
if
apply_cached_metadata
:
cached_metadata
=
self
.
get_cached_metadata_inheritance_tree
(
Location
(
item
[
'location'
])
)
cached_metadata
=
self
.
get_cached_metadata_inheritance_tree
(
location
)
# TODO (cdodge): When the 'split module store' work has been completed, we should remove
# TODO (cdodge): When the 'split module store' work has been completed, we should remove
# the 'metadata_inheritance_tree' parameter
# the 'metadata_inheritance_tree' parameter
...
@@ -505,7 +512,7 @@ class MongoModuleStore(ModuleStoreWriteBase):
...
@@ -505,7 +512,7 @@ class MongoModuleStore(ModuleStoreWriteBase):
mixins
=
self
.
xblock_mixins
,
mixins
=
self
.
xblock_mixins
,
select
=
self
.
xblock_select
,
select
=
self
.
xblock_select
,
)
)
return
system
.
load_item
(
item
[
'location'
]
)
return
system
.
load_item
(
location
)
def
_load_items
(
self
,
items
,
depth
=
0
):
def
_load_items
(
self
,
items
,
depth
=
0
):
"""
"""
...
@@ -779,6 +786,8 @@ class MongoModuleStore(ModuleStoreWriteBase):
...
@@ -779,6 +786,8 @@ class MongoModuleStore(ModuleStoreWriteBase):
location: Something that can be passed to Location
location: Something that can be passed to Location
children: A list of child item identifiers
children: A list of child item identifiers
"""
"""
# Normalize the children to urls
children
=
[
Location
(
child
)
.
url
()
for
child
in
children
]
self
.
_update_single_item
(
location
,
{
'definition.children'
:
children
})
self
.
_update_single_item
(
location
,
{
'definition.children'
:
children
})
# recompute (and update) the metadata inheritance tree which is cached
# recompute (and update) the metadata inheritance tree which is cached
...
@@ -888,5 +897,5 @@ class MongoModuleStore(ModuleStoreWriteBase):
...
@@ -888,5 +897,5 @@ class MongoModuleStore(ModuleStoreWriteBase):
metadata
,
metadata
,
)
)
field_data
=
DbModel
(
kvs
)
field_data
=
KvsFieldData
(
kvs
)
return
field_data
return
field_data
common/lib/xmodule/xmodule/modulestore/split_mongo/caching_descriptor_system.py
View file @
d2a0df41
...
@@ -4,7 +4,7 @@ from xmodule.mako_module import MakoDescriptorSystem
...
@@ -4,7 +4,7 @@ from xmodule.mako_module import MakoDescriptorSystem
from
xmodule.modulestore.locator
import
BlockUsageLocator
,
LocalId
from
xmodule.modulestore.locator
import
BlockUsageLocator
,
LocalId
from
xmodule.error_module
import
ErrorDescriptor
from
xmodule.error_module
import
ErrorDescriptor
from
xmodule.errortracker
import
exc_info_to_str
from
xmodule.errortracker
import
exc_info_to_str
from
xblock.runtime
import
DbModel
from
xblock.runtime
import
KvsFieldData
,
IdReader
from
..exceptions
import
ItemNotFoundError
from
..exceptions
import
ItemNotFoundError
from
.split_mongo_kvs
import
SplitMongoKVS
from
.split_mongo_kvs
import
SplitMongoKVS
from
xblock.fields
import
ScopeIds
from
xblock.fields
import
ScopeIds
...
@@ -12,6 +12,23 @@ from xblock.fields import ScopeIds
...
@@ -12,6 +12,23 @@ from xblock.fields import ScopeIds
log
=
logging
.
getLogger
(
__name__
)
log
=
logging
.
getLogger
(
__name__
)
class
SplitMongoIdReader
(
IdReader
):
"""
An :class:`~xblock.runtime.IdReader` associated with a particular
:class:`.CachingDescriptorSystem`.
"""
def
__init__
(
self
,
system
):
self
.
system
=
system
def
get_definition_id
(
self
,
usage_id
):
usage
=
self
.
system
.
load_item
(
usage_id
)
return
usage
.
definition_locator
def
get_block_type
(
self
,
def_id
):
definition
=
self
.
system
.
modulestore
.
db_connection
.
get_definition
(
def_id
)
return
definition
[
'category'
]
class
CachingDescriptorSystem
(
MakoDescriptorSystem
):
class
CachingDescriptorSystem
(
MakoDescriptorSystem
):
"""
"""
A system that has a cache of a course version's json that it will use to load modules
A system that has a cache of a course version's json that it will use to load modules
...
@@ -33,7 +50,12 @@ class CachingDescriptorSystem(MakoDescriptorSystem):
...
@@ -33,7 +50,12 @@ class CachingDescriptorSystem(MakoDescriptorSystem):
module_data: a dict mapping Location -> json that was cached from the
module_data: a dict mapping Location -> json that was cached from the
underlying modulestore
underlying modulestore
"""
"""
super
(
CachingDescriptorSystem
,
self
)
.
__init__
(
load_item
=
self
.
_load_item
,
**
kwargs
)
super
(
CachingDescriptorSystem
,
self
)
.
__init__
(
id_reader
=
SplitMongoIdReader
(
self
),
field_data
=
None
,
load_item
=
self
.
_load_item
,
**
kwargs
)
self
.
modulestore
=
modulestore
self
.
modulestore
=
modulestore
self
.
course_entry
=
course_entry
self
.
course_entry
=
course_entry
self
.
lazy
=
lazy
self
.
lazy
=
lazy
...
@@ -102,7 +124,7 @@ class CachingDescriptorSystem(MakoDescriptorSystem):
...
@@ -102,7 +124,7 @@ class CachingDescriptorSystem(MakoDescriptorSystem):
json_data
.
get
(
'fields'
,
{}),
json_data
.
get
(
'fields'
,
{}),
json_data
.
get
(
'_inherited_settings'
),
json_data
.
get
(
'_inherited_settings'
),
)
)
field_data
=
DbModel
(
kvs
)
field_data
=
KvsFieldData
(
kvs
)
try
:
try
:
module
=
self
.
construct_xblock_from_class
(
module
=
self
.
construct_xblock_from_class
(
...
...
common/lib/xmodule/xmodule/modulestore/split_mongo/split.py
View file @
d2a0df41
...
@@ -485,7 +485,8 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -485,7 +485,8 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
}
}
"""
"""
course
=
self
.
_lookup_course
(
course_locator
)[
'structure'
]
course
=
self
.
_lookup_course
(
course_locator
)[
'structure'
]
return
{
'original_version'
:
course
[
'original_version'
],
return
{
'original_version'
:
course
[
'original_version'
],
'previous_version'
:
course
[
'previous_version'
],
'previous_version'
:
course
[
'previous_version'
],
'edited_by'
:
course
[
'edited_by'
],
'edited_by'
:
course
[
'edited_by'
],
'edited_on'
:
course
[
'edited_on'
]
'edited_on'
:
course
[
'edited_on'
]
...
@@ -1328,8 +1329,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
...
@@ -1328,8 +1329,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
if
depth
is
None
or
depth
>
0
:
if
depth
is
None
or
depth
>
0
:
depth
=
depth
-
1
if
depth
is
not
None
else
None
depth
=
depth
-
1
if
depth
is
not
None
else
None
for
child
in
block_map
[
block_id
][
'fields'
]
.
get
(
'children'
,
[]):
for
child
in
block_map
[
block_id
][
'fields'
]
.
get
(
'children'
,
[]):
descendent_map
=
self
.
descendants
(
block_map
,
child
,
depth
,
descendent_map
=
self
.
descendants
(
block_map
,
child
,
depth
,
descendent_map
)
descendent_map
)
return
descendent_map
return
descendent_map
...
...
common/lib/xmodule/xmodule/modulestore/tests/test_location.py
View file @
d2a0df41
from
nose.tools
import
assert_equals
,
assert_raises
,
assert_not_equals
# pylint: disable=E0611
import
ddt
from
unittest
import
TestCase
from
xmodule.modulestore
import
Location
from
xmodule.modulestore
import
Location
from
xmodule.modulestore.exceptions
import
InvalidLocationError
from
xmodule.modulestore.exceptions
import
InvalidLocationError
# Pairs for testing the clean* functions.
# The first item in the tuple is the input string.
# The second item in the tuple is what the result of
# replacement should be.
GENERAL_PAIRS
=
[
(
''
,
''
),
(
' '
,
'_'
),
(
'abc,'
,
'abc_'
),
(
'ab fg!@//
\\
aj'
,
'ab_fg_aj'
),
(
u"ab
\xA9
"
,
"ab_"
),
# no unicode allowed for now
]
def
check_string_roundtrip
(
url
):
assert_equals
(
url
,
Location
(
url
)
.
url
())
assert_equals
(
url
,
str
(
Location
(
url
)))
def
test_string_roundtrip
():
check_string_roundtrip
(
"tag://org/course/category/name"
)
check_string_roundtrip
(
"tag://org/course/category/name@revision"
)
@ddt.ddt
class
TestLocations
(
TestCase
):
@ddt.data
(
"tag://org/course/category/name"
,
"tag://org/course/category/name@revision"
)
def
test_string_roundtrip
(
self
,
url
):
self
.
assertEquals
(
url
,
Location
(
url
)
.
url
())
self
.
assertEquals
(
url
,
str
(
Location
(
url
)))
input_dict
=
{
@ddt.data
(
{
'tag'
:
'tag'
,
'tag'
:
'tag'
,
'course'
:
'course'
,
'course'
:
'course'
,
'category'
:
'category'
,
'category'
:
'category'
,
'name'
:
'name'
,
'name'
:
'name'
,
'org'
:
'org'
'org'
:
'org'
}
},
{
also_valid_dict
=
{
'tag'
:
'tag'
,
'tag'
:
'tag'
,
'course'
:
'course'
,
'course'
:
'course'
,
'category'
:
'category'
,
'category'
:
'category'
,
'name'
:
'name:more_name'
,
'name'
:
'name:more_name'
,
'org'
:
'org'
'org'
:
'org'
}
},
[
'tag'
,
'org'
,
'course'
,
'category'
,
'name'
],
"tag://org/course/category/name"
,
input_list
=
[
'tag'
,
'org'
,
'course'
,
'category'
,
'name'
]
"tag://org/course/category/name@revision"
,
u"tag://org/course/category/name"
,
input_str
=
"tag://org/course/category/name"
u"tag://org/course/category/name@revision"
,
input_str_rev
=
"tag://org/course/category/name@revision"
)
def
test_is_valid
(
self
,
loc
):
valid
=
(
input_list
,
input_dict
,
input_str
,
input_str_rev
,
also_valid_dict
)
self
.
assertTrue
(
Location
.
is_valid
(
loc
)
)
invalid_dict
=
{
@ddt.data
(
{
'tag'
:
'tag'
,
'tag'
:
'tag'
,
'course'
:
'course'
,
'course'
:
'course'
,
'category'
:
'category'
,
'category'
:
'category'
,
'name'
:
'name@more_name'
,
'name'
:
'name@more_name'
,
'org'
:
'org'
'org'
:
'org'
}
},
{
invalid_dict2
=
{
'tag'
:
'tag'
,
'tag'
:
'tag'
,
'course'
:
'course'
,
'course'
:
'course'
,
'category'
:
'category'
,
'category'
:
'category'
,
'name'
:
'name '
,
# extra space
'name'
:
'name '
,
# extra space
'org'
:
'org'
'org'
:
'org'
}
},
"foo"
,
invalid
=
(
"foo"
,
[
"foo"
],
[
"foo"
,
"bar"
],
[
"foo"
],
[
"foo"
,
"bar"
],
[
"foo"
,
"bar"
,
"baz"
,
"blat:blat"
,
"foo:bar"
],
# ':' ok in name, not in category
[
"foo"
,
"bar"
,
"baz"
,
"blat:blat"
,
"foo:bar"
],
# ':' ok in name, not in category
"tag://org/course/category/name with spaces@revision"
,
"tag://org/course/category/name with spaces@revision"
,
"tag://org/course/category/name/with/slashes@revision"
,
"tag://org/course/category/name/with/slashes@revision"
,
invalid_dict
,
u"tag://org/course/category/name
\xae
"
,
# No non-ascii characters for now
invalid_dict2
)
u"tag://org/course/category
\xae
/name"
,
# No non-ascii characters for now
)
def
test_is_invalid
(
self
,
loc
):
def
test_is_valid
():
self
.
assertFalse
(
Location
.
is_valid
(
loc
))
for
v
in
valid
:
assert_equals
(
Location
.
is_valid
(
v
),
True
)
for
v
in
invalid
:
assert_equals
(
Location
.
is_valid
(
v
),
False
)
def
test_dict
(
self
):
input_dict
=
{
'tag'
:
'tag'
,
'course'
:
'course'
,
'category'
:
'category'
,
'name'
:
'name'
,
'org'
:
'org'
}
def
test_dict
():
self
.
assertEquals
(
"tag://org/course/category/name"
,
Location
(
input_dict
)
.
url
())
assert_equals
(
"tag://org/course/category/name"
,
Location
(
input_dict
)
.
url
())
self
.
assertEquals
(
dict
(
revision
=
None
,
**
input_dict
),
Location
(
input_dict
)
.
dict
())
assert_equals
(
dict
(
revision
=
None
,
**
input_dict
),
Location
(
input_dict
)
.
dict
())
input_dict
[
'revision'
]
=
'revision'
input_dict
[
'revision'
]
=
'revision'
assert_e
quals
(
"tag://org/course/category/name@revision"
,
Location
(
input_dict
)
.
url
())
self
.
assertE
quals
(
"tag://org/course/category/name@revision"
,
Location
(
input_dict
)
.
url
())
assert_e
quals
(
input_dict
,
Location
(
input_dict
)
.
dict
())
self
.
assertE
quals
(
input_dict
,
Location
(
input_dict
)
.
dict
())
def
test_list
(
self
):
def
test_list
():
input_list
=
[
'tag'
,
'org'
,
'course'
,
'category'
,
'name'
]
assert_e
quals
(
"tag://org/course/category/name"
,
Location
(
input_list
)
.
url
())
self
.
assertE
quals
(
"tag://org/course/category/name"
,
Location
(
input_list
)
.
url
())
assert_e
quals
(
input_list
+
[
None
],
Location
(
input_list
)
.
list
())
self
.
assertE
quals
(
input_list
+
[
None
],
Location
(
input_list
)
.
list
())
input_list
.
append
(
'revision'
)
input_list
.
append
(
'revision'
)
assert_e
quals
(
"tag://org/course/category/name@revision"
,
Location
(
input_list
)
.
url
())
self
.
assertE
quals
(
"tag://org/course/category/name@revision"
,
Location
(
input_list
)
.
url
())
assert_e
quals
(
input_list
,
Location
(
input_list
)
.
list
())
self
.
assertE
quals
(
input_list
,
Location
(
input_list
)
.
list
())
def
test_location
(
self
):
def
test_location
():
input_list
=
[
'tag'
,
'org'
,
'course'
,
'category'
,
'name'
]
input_list
=
[
'tag'
,
'org'
,
'course'
,
'category'
,
'name'
]
assert_equals
(
"tag://org/course/category/name"
,
Location
(
Location
(
input_list
))
.
url
())
self
.
assertEquals
(
"tag://org/course/category/name"
,
Location
(
Location
(
input_list
))
.
url
())
def
test_none
():
def
test_none
(
self
):
assert_equals
([
None
]
*
6
,
Location
(
None
)
.
list
())
self
.
assertEquals
([
None
]
*
6
,
Location
(
None
)
.
list
())
def
test_invalid_locations
():
assert_raises
(
InvalidLocationError
,
Location
,
"foo"
)
assert_raises
(
InvalidLocationError
,
Location
,
[
"foo"
,
"bar"
])
assert_raises
(
InvalidLocationError
,
Location
,
[
"foo"
,
"bar"
,
"baz"
,
"blat/blat"
,
"foo"
])
assert_raises
(
InvalidLocationError
,
Location
,
[
"foo"
,
"bar"
,
"baz"
,
"blat"
,
"foo/bar"
])
assert_raises
(
InvalidLocationError
,
Location
,
"tag://org/course/category/name with spaces@revision"
)
assert_raises
(
InvalidLocationError
,
Location
,
"tag://org/course/category/name/revision"
)
@ddt.data
(
"foo"
,
[
"foo"
,
"bar"
],
[
"foo"
,
"bar"
,
"baz"
,
"blat/blat"
,
"foo"
],
[
"foo"
,
"bar"
,
"baz"
,
"blat"
,
"foo/bar"
],
"tag://org/course/category/name with spaces@revision"
,
"tag://org/course/category/name/revision"
,
)
def
test_invalid_locations
(
self
,
loc
):
with
self
.
assertRaises
(
InvalidLocationError
):
Location
(
loc
)
def
test_equality
(
):
def
test_equality
(
self
):
assert_e
quals
(
self
.
assertE
quals
(
Location
(
'tag'
,
'org'
,
'course'
,
'category'
,
'name'
),
Location
(
'tag'
,
'org'
,
'course'
,
'category'
,
'name'
),
Location
(
'tag'
,
'org'
,
'course'
,
'category'
,
'name'
)
Location
(
'tag'
,
'org'
,
'course'
,
'category'
,
'name'
)
)
)
assert_not_e
quals
(
self
.
assertNotE
quals
(
Location
(
'tag'
,
'org'
,
'course'
,
'category'
,
'name1'
),
Location
(
'tag'
,
'org'
,
'course'
,
'category'
,
'name1'
),
Location
(
'tag'
,
'org'
,
'course'
,
'category'
,
'name'
)
Location
(
'tag'
,
'org'
,
'course'
,
'category'
,
'name'
)
)
)
# All the cleaning functions should do the same thing with these
@ddt.data
(
general_pairs
=
[(
''
,
''
),
(
' '
,
'_'
),
(
'abc,'
,
'abc_'
),
(
'ab fg!@//
\\
aj'
,
'ab_fg_aj'
),
(
u"ab
\xA9
"
,
"ab_"
),
# no unicode allowed for now
]
def
test_clean
():
pairs
=
general_pairs
+
[
(
'a:b'
,
'a_b'
),
# no colons in non-name components
(
'a:b'
,
'a_b'
),
# no colons in non-name components
(
'a-b'
,
'a-b'
),
# dashes ok
(
'a-b'
,
'a-b'
),
# dashes ok
(
'a.b'
,
'a.b'
),
# dot ok
(
'a.b'
,
'a.b'
),
# dot ok
]
*
GENERAL_PAIRS
for
input
,
output
in
pairs
:
)
assert_equals
(
Location
.
clean
(
input
),
output
)
def
test_clean
(
self
,
pair
):
self
.
assertEquals
(
Location
.
clean
(
pair
[
0
]),
pair
[
1
])
def
test_clean_for_url_name
():
@ddt.data
(
pairs
=
general_pairs
+
[
(
'a:b'
,
'a:b'
),
# colons ok in names
(
'a:b'
,
'a:b'
),
# colons ok in names
(
'a-b'
,
'a-b'
),
# dashes ok in names
(
'a-b'
,
'a-b'
),
# dashes ok in names
(
'a.b'
,
'a.b'
),
# dot ok in names
(
'a.b'
,
'a.b'
),
# dot ok in names
]
*
GENERAL_PAIRS
for
input
,
output
in
pairs
:
)
assert_equals
(
Location
.
clean_for_url_name
(
input
),
output
)
def
test_clean_for_url_name
(
self
,
pair
):
self
.
assertEquals
(
Location
.
clean_for_url_name
(
pair
[
0
]),
pair
[
1
])
def
test_clean_for_html
():
@ddt.data
(
pairs
=
general_pairs
+
[
(
"a:b"
,
"a_b"
),
# no colons for html use
(
"a:b"
,
"a_b"
),
# no colons for html use
(
"a-b"
,
"a-b"
),
# dashes ok (though need to be replaced in various use locations. ugh.)
(
"a-b"
,
"a-b"
),
# dashes ok (though need to be replaced in various use locations. ugh.)
(
'a.b'
,
'a_b'
),
# no dots.
(
'a.b'
,
'a_b'
),
# no dots.
]
*
GENERAL_PAIRS
for
input
,
output
in
pairs
:
)
assert_equals
(
Location
.
clean_for_html
(
input
),
output
)
def
test_clean_for_html
(
self
,
pair
):
self
.
assertEquals
(
Location
.
clean_for_html
(
pair
[
0
]),
pair
[
1
])
def
test_html_id
(
):
def
test_html_id
(
self
):
loc
=
Location
(
"tag://org/course/cat/name:more_name@rev"
)
loc
=
Location
(
"tag://org/course/cat/name:more_name@rev"
)
assert_equals
(
loc
.
html_id
(),
"tag-org-course-cat-name_more_name-rev"
)
self
.
assertEquals
(
loc
.
html_id
(),
"tag-org-course-cat-name_more_name-rev"
)
def
test_course_id
(
):
def
test_course_id
(
self
):
loc
=
Location
(
'i4x'
,
'mitX'
,
'103'
,
'course'
,
'test2'
)
loc
=
Location
(
'i4x'
,
'mitX'
,
'103'
,
'course'
,
'test2'
)
assert_e
quals
(
'mitX/103/test2'
,
loc
.
course_id
)
self
.
assertE
quals
(
'mitX/103/test2'
,
loc
.
course_id
)
loc
=
Location
(
'i4x'
,
'mitX'
,
'103'
,
'_not_a_course'
,
'test2'
)
loc
=
Location
(
'i4x'
,
'mitX'
,
'103'
,
'_not_a_course'
,
'test2'
)
with
assert_raises
(
InvalidLocationError
):
with
self
.
assertRaises
(
InvalidLocationError
):
loc
.
course_id
loc
.
course_id
# pylint: disable=pointless-statement
def
test_replacement
(
self
):
self
.
assertEquals
(
Location
(
't://o/c/c/n@r'
)
.
_replace
(
name
=
'new_name'
),
Location
(
't://o/c/c/new_name@r'
),
)
with
self
.
assertRaises
(
InvalidLocationError
):
Location
(
't://o/c/c/n@r'
)
.
_replace
(
name
=
u'name
\xae
'
)
@ddt.data
(
'org'
,
'course'
,
'category'
,
'name'
,
'revision'
)
def
test_immutable
(
self
,
attr
):
loc
=
Location
(
't://o/c/c/n@r'
)
with
self
.
assertRaises
(
AttributeError
):
setattr
(
loc
,
attr
,
attr
)
common/lib/xmodule/xmodule/modulestore/tests/test_mongo.py
View file @
d2a0df41
...
@@ -285,13 +285,6 @@ class TestMongoKeyValueStore(object):
...
@@ -285,13 +285,6 @@ class TestMongoKeyValueStore(object):
self
.
metadata
=
{
'meta'
:
'meta_val'
}
self
.
metadata
=
{
'meta'
:
'meta_val'
}
self
.
kvs
=
MongoKeyValueStore
(
self
.
data
,
self
.
children
,
self
.
metadata
)
self
.
kvs
=
MongoKeyValueStore
(
self
.
data
,
self
.
children
,
self
.
metadata
)
def
_check_read
(
self
,
key
,
expected_value
):
"""
Asserts the get and has methods.
"""
assert_equals
(
expected_value
,
self
.
kvs
.
get
(
key
))
assert
self
.
kvs
.
has
(
key
)
def
test_read
(
self
):
def
test_read
(
self
):
assert_equals
(
self
.
data
[
'foo'
],
self
.
kvs
.
get
(
KeyValueStore
.
Key
(
Scope
.
content
,
None
,
None
,
'foo'
)))
assert_equals
(
self
.
data
[
'foo'
],
self
.
kvs
.
get
(
KeyValueStore
.
Key
(
Scope
.
content
,
None
,
None
,
'foo'
)))
assert_equals
(
self
.
children
,
self
.
kvs
.
get
(
KeyValueStore
.
Key
(
Scope
.
children
,
None
,
None
,
'children'
)))
assert_equals
(
self
.
children
,
self
.
kvs
.
get
(
KeyValueStore
.
Key
(
Scope
.
children
,
None
,
None
,
'children'
)))
...
...
common/lib/xmodule/xmodule/modulestore/xml.py
View file @
d2a0df41
import
hashlib
import
hashlib
import
itertools
import
json
import
json
import
logging
import
logging
import
os
import
os
...
@@ -17,17 +18,18 @@ from xmodule.error_module import ErrorDescriptor
...
@@ -17,17 +18,18 @@ from xmodule.error_module import ErrorDescriptor
from
xmodule.errortracker
import
make_error_tracker
,
exc_info_to_str
from
xmodule.errortracker
import
make_error_tracker
,
exc_info_to_str
from
xmodule.course_module
import
CourseDescriptor
from
xmodule.course_module
import
CourseDescriptor
from
xmodule.mako_module
import
MakoDescriptorSystem
from
xmodule.mako_module
import
MakoDescriptorSystem
from
xmodule.x_module
import
XMLParsingSystem
,
prefer_xmodules
from
xmodule.x_module
import
XMLParsingSystem
,
prefer_xmodules
,
policy_key
from
xmodule.html_module
import
HtmlDescriptor
from
xmodule.html_module
import
HtmlDescriptor
from
xblock.core
import
XBlock
from
xblock.core
import
XBlock
from
xblock.fields
import
ScopeIds
from
xblock.fields
import
ScopeIds
from
xblock.field_data
import
DictFieldData
from
xblock.field_data
import
DictFieldData
from
xblock.runtime
import
DictKeyValueStore
,
IdReader
,
IdGenerator
from
.
import
ModuleStoreReadBase
,
Location
,
XML_MODULESTORE_TYPE
from
.
import
ModuleStoreReadBase
,
Location
,
XML_MODULESTORE_TYPE
from
.exceptions
import
ItemNotFoundError
from
.exceptions
import
ItemNotFoundError
from
.inheritance
import
compute_inherited_metadata
from
.inheritance
import
compute_inherited_metadata
,
inheriting_field_data
edx_xml_parser
=
etree
.
XMLParser
(
dtd_validation
=
False
,
load_dtd
=
False
,
edx_xml_parser
=
etree
.
XMLParser
(
dtd_validation
=
False
,
load_dtd
=
False
,
remove_comments
=
True
,
remove_blank_text
=
True
)
remove_comments
=
True
,
remove_blank_text
=
True
)
...
@@ -49,7 +51,7 @@ def clean_out_mako_templating(xml_string):
...
@@ -49,7 +51,7 @@ def clean_out_mako_templating(xml_string):
class
ImportSystem
(
XMLParsingSystem
,
MakoDescriptorSystem
):
class
ImportSystem
(
XMLParsingSystem
,
MakoDescriptorSystem
):
def
__init__
(
self
,
xmlstore
,
course_id
,
course_dir
,
def
__init__
(
self
,
xmlstore
,
course_id
,
course_dir
,
error_tracker
,
parent_tracker
,
error_tracker
,
parent_tracker
,
load_error_modules
=
True
,
**
kwargs
):
load_error_modules
=
True
,
id_reader
=
None
,
**
kwargs
):
"""
"""
A class that handles loading from xml. Does some munging to ensure that
A class that handles loading from xml. Does some munging to ensure that
all elements have unique slugs.
all elements have unique slugs.
...
@@ -59,6 +61,10 @@ class ImportSystem(XMLParsingSystem, MakoDescriptorSystem):
...
@@ -59,6 +61,10 @@ class ImportSystem(XMLParsingSystem, MakoDescriptorSystem):
self
.
unnamed
=
defaultdict
(
int
)
# category -> num of new url_names for that category
self
.
unnamed
=
defaultdict
(
int
)
# category -> num of new url_names for that category
self
.
used_names
=
defaultdict
(
set
)
# category -> set of used url_names
self
.
used_names
=
defaultdict
(
set
)
# category -> set of used url_names
self
.
org
,
self
.
course
,
self
.
url_name
=
course_id
.
split
(
'/'
)
self
.
org
,
self
.
course
,
self
.
url_name
=
course_id
.
split
(
'/'
)
if
id_reader
is
None
:
id_reader
=
LocationReader
()
id_generator
=
CourseLocationGenerator
(
self
.
org
,
self
.
course
)
# cdodge: adding the course_id as passed in for later reference rather than having to recomine the org/course/url_name
# cdodge: adding the course_id as passed in for later reference rather than having to recomine the org/course/url_name
self
.
course_id
=
course_id
self
.
course_id
=
course_id
self
.
load_error_modules
=
load_error_modules
self
.
load_error_modules
=
load_error_modules
...
@@ -165,8 +171,10 @@ class ImportSystem(XMLParsingSystem, MakoDescriptorSystem):
...
@@ -165,8 +171,10 @@ class ImportSystem(XMLParsingSystem, MakoDescriptorSystem):
make_name_unique
(
xml_data
)
make_name_unique
(
xml_data
)
descriptor
=
create_block_from_xml
(
descriptor
=
create_block_from_xml
(
etree
.
tostring
(
xml_data
,
encoding
=
'unicode'
),
self
,
self
.
org
,
etree
.
tostring
(
xml_data
,
encoding
=
'unicode'
),
self
.
course
,
xmlstore
.
default_class
)
self
,
id_generator
,
)
except
Exception
as
err
:
except
Exception
as
err
:
if
not
self
.
load_error_modules
:
if
not
self
.
load_error_modules
:
raise
raise
...
@@ -174,30 +182,34 @@ class ImportSystem(XMLParsingSystem, MakoDescriptorSystem):
...
@@ -174,30 +182,34 @@ class ImportSystem(XMLParsingSystem, MakoDescriptorSystem):
# Didn't load properly. Fall back on loading as an error
# Didn't load properly. Fall back on loading as an error
# descriptor. This should never error due to formatting.
# descriptor. This should never error due to formatting.
msg
=
"Error loading from xml. "
+
unicode
(
err
)[:
200
]
msg
=
"Error loading from xml.
%
s"
log
.
warning
(
msg
)
log
.
warning
(
msg
,
unicode
(
err
)[:
200
],
# Normally, we don't want lots of exception traces in our logs from common
# Normally, we don't want lots of exception traces in our logs from common
# content problems. But if you're debugging the xml loading code itself,
# content problems. But if you're debugging the xml loading code itself,
# uncomment the next line.
# uncomment the next line.
# log.exception(msg)
# exc_info=True
)
msg
=
msg
%
(
unicode
(
err
)[:
200
])
self
.
error_tracker
(
msg
)
self
.
error_tracker
(
msg
)
err_msg
=
msg
+
"
\n
"
+
exc_info_to_str
(
sys
.
exc_info
())
err_msg
=
msg
+
"
\n
"
+
exc_info_to_str
(
sys
.
exc_info
())
descriptor
=
ErrorDescriptor
.
from_xml
(
descriptor
=
ErrorDescriptor
.
from_xml
(
xml
,
xml
,
self
,
self
,
self
.
org
,
id_generator
,
self
.
course
,
err_msg
err_msg
)
)
descriptor
.
data_dir
=
course_dir
descriptor
.
data_dir
=
course_dir
xmlstore
.
modules
[
course_id
][
descriptor
.
location
]
=
descriptor
xmlstore
.
modules
[
course_id
][
descriptor
.
scope_ids
.
usage_id
]
=
descriptor
if
hasattr
(
descriptor
,
'children'
)
:
if
descriptor
.
has_children
:
for
child
in
descriptor
.
get_children
():
for
child
in
descriptor
.
get_children
():
parent_tracker
.
add_parent
(
child
.
location
,
descriptor
.
location
)
parent_tracker
.
add_parent
(
child
.
scope_ids
.
usage_id
,
descriptor
.
scope_ids
.
usage_id
)
# After setting up the descriptor, save any changes that we have
# After setting up the descriptor, save any changes that we have
# made to attributes on the descriptor to the underlying KeyValueStore.
# made to attributes on the descriptor to the underlying KeyValueStore.
...
@@ -205,50 +217,94 @@ class ImportSystem(XMLParsingSystem, MakoDescriptorSystem):
...
@@ -205,50 +217,94 @@ class ImportSystem(XMLParsingSystem, MakoDescriptorSystem):
return
descriptor
return
descriptor
render_template
=
lambda
template
,
context
:
u''
render_template
=
lambda
template
,
context
:
u''
# TODO (vshnayder): we are somewhat architecturally confused in the loading code:
# TODO (vshnayder): we are somewhat architecturally confused in the loading code:
# load_item should actually be get_instance, because it expects the course-specific
# load_item should actually be get_instance, because it expects the course-specific
# policy to be loaded. For now, just add the course_id here...
# policy to be loaded. For now, just add the course_id here...
load_item
=
lambda
location
:
xmlstore
.
get_instance
(
course_id
,
location
)
def
load_item
(
location
):
return
xmlstore
.
get_instance
(
course_id
,
Location
(
location
))
resources_fs
=
OSFS
(
xmlstore
.
data_dir
/
course_dir
)
resources_fs
=
OSFS
(
xmlstore
.
data_dir
/
course_dir
)
super
(
ImportSystem
,
self
)
.
__init__
(
super
(
ImportSystem
,
self
)
.
__init__
(
load_item
=
load_item
,
load_item
=
load_item
,
resources_fs
=
resources_fs
,
resources_fs
=
resources_fs
,
render_template
=
render_template
,
render_template
=
render_template
,
error_tracker
=
error_tracker
,
error_tracker
=
error_tracker
,
process_xml
=
process_xml
,
process_xml
=
process_xml
,
id_reader
=
id_reader
,
**
kwargs
**
kwargs
)
)
# id_generator is ignored, because each ImportSystem is already local to
# a course, and has it's own id_generator already in place
def
add_node_as_child
(
self
,
block
,
node
,
id_generator
):
child_block
=
self
.
process_xml
(
etree
.
tostring
(
node
))
block
.
children
.
append
(
child_block
.
scope_ids
.
usage_id
)
def
create_block_from_xml
(
xml_data
,
system
,
org
=
None
,
course
=
None
,
default_class
=
None
):
class
LocationReader
(
IdReader
):
"""
"""
Create an XBlock instance from XML data.
IdReader for definition and usage ids that are Locations
"""
def
get_definition_id
(
self
,
usage_id
):
return
usage_id
`xml_data' is a string containing valid xml.
def
get_block_type
(
self
,
def_id
):
location
=
def_id
return
location
.
category
`system` is an XMLParsingSystem.
`org` and `course` are optional strings that will be used in the generated
class
CourseLocationGenerator
(
IdGenerator
):
block's url identifiers.
"""
IdGenerator for Location-based definition ids and usage ids
based within a course
"""
def
__init__
(
self
,
org
,
course
):
self
.
org
=
org
self
.
course
=
course
self
.
autogen_ids
=
itertools
.
count
(
0
)
def
create_usage
(
self
,
def_id
):
return
Location
(
def_id
)
def
create_definition
(
self
,
block_type
,
slug
=
None
):
assert
block_type
is
not
None
if
slug
is
None
:
slug
=
'autogen_{}_{}'
.
format
(
block_type
,
self
.
autogen_ids
.
next
())
location
=
Location
(
'i4x'
,
self
.
org
,
self
.
course
,
block_type
,
slug
)
return
location
def
create_block_from_xml
(
xml_data
,
system
,
id_generator
):
"""
Create an XBlock instance from XML data.
`default_class` is the class to instantiate of the XML indicates a class
Args:
that can't be loaded.
xml_data (string): A string containing valid xml.
system (XMLParsingSystem): The :class:`.XMLParsingSystem` used to connect the block
to the outside world.
id_generator (IdGenerator): An :class:`~xblock.runtime.IdGenerator` that
will be used to construct the usage_id and definition_id for the block.
Returns the fully instantiated XBlock.
Returns:
XBlock: The fully instantiated :class:`~xblock.core.XBlock`.
"""
"""
node
=
etree
.
fromstring
(
xml_data
)
node
=
etree
.
fromstring
(
xml_data
)
raw_class
=
XBlock
.
load_class
(
node
.
tag
,
default_class
,
select
=
prefer_xmodules
)
raw_class
=
system
.
load_block_type
(
node
.
tag
)
xblock_class
=
system
.
mixologist
.
mix
(
raw_class
)
xblock_class
=
system
.
mixologist
.
mix
(
raw_class
)
# leave next line commented out - useful for low-level debugging
# leave next line commented out - useful for low-level debugging
# log.debug('[create_block_from_xml] tag=%s, class=%s' % (node.tag, xblock_class))
# log.debug('[create_block_from_xml] tag=%s, class=%s' % (node.tag, xblock_class))
url_name
=
node
.
get
(
'url_name'
,
node
.
get
(
'slug'
))
block_type
=
node
.
tag
location
=
Location
(
'i4x'
,
org
,
course
,
node
.
tag
,
url_name
)
url_name
=
node
.
get
(
'url_name'
)
def_id
=
id_generator
.
create_definition
(
block_type
,
url_name
)
usage_id
=
id_generator
.
create_usage
(
def_id
)
scope_ids
=
ScopeIds
(
None
,
location
.
category
,
location
,
location
)
scope_ids
=
ScopeIds
(
None
,
block_type
,
def_id
,
usage_id
)
xblock
=
xblock_class
.
parse_xml
(
node
,
system
,
scope_ids
)
xblock
=
xblock_class
.
parse_xml
(
node
,
system
,
scope_ids
,
id_generator
)
return
xblock
return
xblock
...
@@ -265,10 +321,8 @@ class ParentTracker(object):
...
@@ -265,10 +321,8 @@ class ParentTracker(object):
"""
"""
Add a parent of child location to the set of parents. Duplicate calls have no effect.
Add a parent of child location to the set of parents. Duplicate calls have no effect.
child and parent must be
something that can be passed to Location
.
child and parent must be
:class:`.Location` instances
.
"""
"""
child
=
Location
(
child
)
parent
=
Location
(
parent
)
s
=
self
.
_parents
.
setdefault
(
child
,
set
())
s
=
self
.
_parents
.
setdefault
(
child
,
set
())
s
.
add
(
parent
)
s
.
add
(
parent
)
...
@@ -276,7 +330,6 @@ class ParentTracker(object):
...
@@ -276,7 +330,6 @@ class ParentTracker(object):
"""
"""
returns True iff child has some parents.
returns True iff child has some parents.
"""
"""
child
=
Location
(
child
)
return
child
in
self
.
_parents
return
child
in
self
.
_parents
def
make_known
(
self
,
location
):
def
make_known
(
self
,
location
):
...
@@ -288,7 +341,6 @@ class ParentTracker(object):
...
@@ -288,7 +341,6 @@ class ParentTracker(object):
"""
"""
Return a list of the parents of this child. If not is_known(child), will throw a KeyError
Return a list of the parents of this child. If not is_known(child), will throw a KeyError
"""
"""
child
=
Location
(
child
)
return
list
(
self
.
_parents
[
child
])
return
list
(
self
.
_parents
[
child
])
...
@@ -326,6 +378,9 @@ class XMLModuleStore(ModuleStoreReadBase):
...
@@ -326,6 +378,9 @@ class XMLModuleStore(ModuleStoreReadBase):
self
.
parent_trackers
=
defaultdict
(
ParentTracker
)
self
.
parent_trackers
=
defaultdict
(
ParentTracker
)
# All field data will be stored in an inheriting field data.
self
.
field_data
=
inheriting_field_data
(
kvs
=
DictKeyValueStore
())
# If we are specifically asked for missing courses, that should
# If we are specifically asked for missing courses, that should
# be an error. If we are asked for "all" courses, find the ones
# be an error. If we are asked for "all" courses, find the ones
# that have a course.xml. We sort the dirs in alpha order so we always
# that have a course.xml. We sort the dirs in alpha order so we always
...
@@ -357,8 +412,8 @@ class XMLModuleStore(ModuleStoreReadBase):
...
@@ -357,8 +412,8 @@ class XMLModuleStore(ModuleStoreReadBase):
if
course_descriptor
is
not
None
and
not
isinstance
(
course_descriptor
,
ErrorDescriptor
):
if
course_descriptor
is
not
None
and
not
isinstance
(
course_descriptor
,
ErrorDescriptor
):
self
.
courses
[
course_dir
]
=
course_descriptor
self
.
courses
[
course_dir
]
=
course_descriptor
self
.
_location_errors
[
course_descriptor
.
location
]
=
errorlog
self
.
_location_errors
[
course_descriptor
.
scope_ids
.
usage_id
]
=
errorlog
self
.
parent_trackers
[
course_descriptor
.
id
]
.
make_known
(
course_descriptor
.
location
)
self
.
parent_trackers
[
course_descriptor
.
id
]
.
make_known
(
course_descriptor
.
scope_ids
.
usage_id
)
else
:
else
:
# Didn't load course. Instead, save the errors elsewhere.
# Didn't load course. Instead, save the errors elsewhere.
self
.
errored_courses
[
course_dir
]
=
errorlog
self
.
errored_courses
[
course_dir
]
=
errorlog
...
@@ -368,7 +423,8 @@ class XMLModuleStore(ModuleStoreReadBase):
...
@@ -368,7 +423,8 @@ class XMLModuleStore(ModuleStoreReadBase):
String representation - for debugging
String representation - for debugging
'''
'''
return
'<XMLModuleStore data_dir=
%
r,
%
d courses,
%
d modules>'
%
(
return
'<XMLModuleStore data_dir=
%
r,
%
d courses,
%
d modules>'
%
(
self
.
data_dir
,
len
(
self
.
courses
),
len
(
self
.
modules
))
self
.
data_dir
,
len
(
self
.
courses
),
len
(
self
.
modules
)
)
def
load_policy
(
self
,
policy_path
,
tracker
):
def
load_policy
(
self
,
policy_path
,
tracker
):
"""
"""
...
@@ -451,6 +507,10 @@ class XMLModuleStore(ModuleStoreReadBase):
...
@@ -451,6 +507,10 @@ class XMLModuleStore(ModuleStoreReadBase):
"(or 'name') set. Set url_name."
)
"(or 'name') set. Set url_name."
)
course_id
=
CourseDescriptor
.
make_id
(
org
,
course
,
url_name
)
course_id
=
CourseDescriptor
.
make_id
(
org
,
course
,
url_name
)
def
get_policy
(
usage_id
):
return
policy
.
get
(
policy_key
(
usage_id
),
{})
system
=
ImportSystem
(
system
=
ImportSystem
(
xmlstore
=
self
,
xmlstore
=
self
,
course_id
=
course_id
,
course_id
=
course_id
,
...
@@ -458,9 +518,11 @@ class XMLModuleStore(ModuleStoreReadBase):
...
@@ -458,9 +518,11 @@ class XMLModuleStore(ModuleStoreReadBase):
error_tracker
=
tracker
,
error_tracker
=
tracker
,
parent_tracker
=
self
.
parent_trackers
[
course_id
],
parent_tracker
=
self
.
parent_trackers
[
course_id
],
load_error_modules
=
self
.
load_error_modules
,
load_error_modules
=
self
.
load_error_modules
,
policy
=
policy
,
get_policy
=
get_
policy
,
mixins
=
self
.
xblock_mixins
,
mixins
=
self
.
xblock_mixins
,
default_class
=
self
.
default_class
,
select
=
self
.
xblock_select
,
select
=
self
.
xblock_select
,
field_data
=
self
.
field_data
,
)
)
course_descriptor
=
system
.
process_xml
(
etree
.
tostring
(
course_data
,
encoding
=
'unicode'
))
course_descriptor
=
system
.
process_xml
(
etree
.
tostring
(
course_data
,
encoding
=
'unicode'
))
...
@@ -508,7 +570,7 @@ class XMLModuleStore(ModuleStoreReadBase):
...
@@ -508,7 +570,7 @@ class XMLModuleStore(ModuleStoreReadBase):
html
=
f
.
read
()
.
decode
(
'utf-8'
)
html
=
f
.
read
()
.
decode
(
'utf-8'
)
# tabs are referenced in policy.json through a 'slug' which is just the filename without the .html suffix
# tabs are referenced in policy.json through a 'slug' which is just the filename without the .html suffix
slug
=
os
.
path
.
splitext
(
os
.
path
.
basename
(
filepath
))[
0
]
slug
=
os
.
path
.
splitext
(
os
.
path
.
basename
(
filepath
))[
0
]
loc
=
Location
(
'i4x'
,
course_descriptor
.
location
.
org
,
course_descriptor
.
location
.
course
,
category
,
slug
)
loc
=
course_descriptor
.
scope_ids
.
usage_id
.
_replace
(
category
=
category
,
name
=
slug
)
module
=
system
.
construct_xblock_from_class
(
module
=
system
.
construct_xblock_from_class
(
HtmlDescriptor
,
HtmlDescriptor
,
# We're loading a descriptor, so student_id is meaningless
# We're loading a descriptor, so student_id is meaningless
...
@@ -526,7 +588,7 @@ class XMLModuleStore(ModuleStoreReadBase):
...
@@ -526,7 +588,7 @@ class XMLModuleStore(ModuleStoreReadBase):
module
.
display_name
=
tab
[
'name'
]
module
.
display_name
=
tab
[
'name'
]
module
.
data_dir
=
course_dir
module
.
data_dir
=
course_dir
module
.
save
()
module
.
save
()
self
.
modules
[
course_descriptor
.
id
][
module
.
location
]
=
module
self
.
modules
[
course_descriptor
.
id
][
module
.
scope_ids
.
usage_id
]
=
module
except
Exception
,
e
:
except
Exception
,
e
:
logging
.
exception
(
"Failed to load
%
s. Skipping...
\
logging
.
exception
(
"Failed to load
%
s. Skipping...
\
Exception:
%
s"
,
filepath
,
unicode
(
e
))
Exception:
%
s"
,
filepath
,
unicode
(
e
))
...
...
common/lib/xmodule/xmodule/modulestore/xml_importer.py
View file @
d2a0df41
...
@@ -310,9 +310,7 @@ def import_module(
...
@@ -310,9 +310,7 @@ def import_module(
source_course_location
,
dest_course_location
,
allow_not_found
=
False
,
source_course_location
,
dest_course_location
,
allow_not_found
=
False
,
do_import_static
=
True
):
do_import_static
=
True
):
logging
.
debug
(
'processing import of module {url}...'
.
format
(
logging
.
debug
(
'processing import of module {}...'
.
format
(
module
.
location
.
url
()))
url
=
module
.
location
.
url
()
))
content
=
{}
content
=
{}
for
field
in
module
.
fields
.
values
():
for
field
in
module
.
fields
.
values
():
...
@@ -393,10 +391,10 @@ def import_course_draft(
...
@@ -393,10 +391,10 @@ def import_course_draft(
xmlstore
=
xml_module_store
,
xmlstore
=
xml_module_store
,
course_id
=
target_location_namespace
.
course_id
,
course_id
=
target_location_namespace
.
course_id
,
course_dir
=
draft_course_dir
,
course_dir
=
draft_course_dir
,
policy
=
{},
error_tracker
=
errorlog
.
tracker
,
error_tracker
=
errorlog
.
tracker
,
parent_tracker
=
ParentTracker
(),
parent_tracker
=
ParentTracker
(),
load_error_modules
=
False
,
load_error_modules
=
False
,
field_data
=
None
,
)
)
# now walk the /vertical directory where each file in there
# now walk the /vertical directory where each file in there
...
...
common/lib/xmodule/xmodule/peer_grading_module.py
View file @
d2a0df41
import
json
import
json
import
logging
import
logging
from
lxml
import
etree
from
datetime
import
datetime
from
datetime
import
datetime
from
lxml
import
etree
from
pkg_resources
import
resource_string
from
pkg_resources
import
resource_string
from
.capa_module
import
ComplexEncoder
from
xblock.fields
import
Dict
,
String
,
Scope
,
Boolean
,
Float
,
Reference
from
.x_module
import
XModule
,
module_attr
from
.raw_module
import
RawDescriptor
from
.modulestore.exceptions
import
ItemNotFoundError
,
NoPathToItem
from
.timeinfo
import
TimeInfo
from
.util.duedate
import
get_extended_due_date
from
xblock.fields
import
Dict
,
String
,
Scope
,
Boolean
,
Float
from
xmodule.fields
import
Date
,
Timedelta
from
xmodule.capa_module
import
ComplexEncoder
from
xmodule.fields
import
Date
,
Timedelta
from
xmodule.modulestore
import
Location
from
xmodule.modulestore.exceptions
import
ItemNotFoundError
,
NoPathToItem
from
xmodule.raw_module
import
RawDescriptor
from
xmodule.timeinfo
import
TimeInfo
from
xmodule.util.duedate
import
get_extended_due_date
from
xmodule.x_module
import
XModule
,
module_attr
from
xmodule.open_ended_grading_classes.peer_grading_service
import
PeerGradingService
,
GradingServiceError
,
MockPeerGradingService
from
xmodule.open_ended_grading_classes.peer_grading_service
import
PeerGradingService
,
GradingServiceError
,
MockPeerGradingService
from
open_ended_grading_classes
import
combined_open_ended_rubric
from
open_ended_grading_classes
import
combined_open_ended_rubric
from
django.utils.timezone
import
UTC
from
django.utils.timezone
import
UTC
...
@@ -32,7 +33,7 @@ class PeerGradingFields(object):
...
@@ -32,7 +33,7 @@ class PeerGradingFields(object):
default
=
False
,
default
=
False
,
scope
=
Scope
.
settings
scope
=
Scope
.
settings
)
)
link_to_location
=
String
(
link_to_location
=
Reference
(
display_name
=
"Link to Problem Location"
,
display_name
=
"Link to Problem Location"
,
help
=
'The location of the problem being graded. Only used when "Show Single Problem" is True.'
,
help
=
'The location of the problem being graded. Only used when "Show Single Problem" is True.'
,
default
=
""
,
default
=
""
,
...
@@ -560,7 +561,7 @@ class PeerGradingModule(PeerGradingFields, XModule):
...
@@ -560,7 +561,7 @@ class PeerGradingModule(PeerGradingFields, XModule):
good_problem_list
=
[]
good_problem_list
=
[]
for
problem
in
problem_list
:
for
problem
in
problem_list
:
problem_location
=
problem
[
'location'
]
problem_location
=
Location
(
problem
[
'location'
])
try
:
try
:
descriptor
=
self
.
_find_corresponding_module_for_location
(
problem_location
)
descriptor
=
self
.
_find_corresponding_module_for_location
(
problem_location
)
except
(
NoPathToItem
,
ItemNotFoundError
):
except
(
NoPathToItem
,
ItemNotFoundError
):
...
@@ -608,10 +609,10 @@ class PeerGradingModule(PeerGradingFields, XModule):
...
@@ -608,10 +609,10 @@ class PeerGradingModule(PeerGradingFields, XModule):
log
.
error
(
log
.
error
(
"Peer grading problem in peer_grading_module called with no get parameters, but use_for_single_location is False."
)
"Peer grading problem in peer_grading_module called with no get parameters, but use_for_single_location is False."
)
return
{
'html'
:
""
,
'success'
:
False
}
return
{
'html'
:
""
,
'success'
:
False
}
problem_location
=
self
.
link_to_location
problem_location
=
Location
(
self
.
link_to_location
)
elif
data
.
get
(
'location'
)
is
not
None
:
elif
data
.
get
(
'location'
)
is
not
None
:
problem_location
=
data
.
get
(
'location'
)
problem_location
=
Location
(
data
.
get
(
'location'
)
)
module
=
self
.
_find_corresponding_module_for_location
(
problem_location
)
module
=
self
.
_find_corresponding_module_for_location
(
problem_location
)
...
@@ -660,7 +661,7 @@ class PeerGradingDescriptor(PeerGradingFields, RawDescriptor):
...
@@ -660,7 +661,7 @@ class PeerGradingDescriptor(PeerGradingFields, RawDescriptor):
metadata_translations
=
{
metadata_translations
=
{
'is_graded'
:
'graded'
,
'is_graded'
:
'graded'
,
'attempts'
:
'max_attempts'
,
'attempts'
:
'max_attempts'
,
'due_data'
:
'due'
'due_data'
:
'due'
}
}
@property
@property
...
...
common/lib/xmodule/xmodule/seq_module.py
View file @
d2a0df41
...
@@ -138,7 +138,8 @@ class SequenceDescriptor(SequenceFields, MakoModuleDescriptor, XmlDescriptor):
...
@@ -138,7 +138,8 @@ class SequenceDescriptor(SequenceFields, MakoModuleDescriptor, XmlDescriptor):
children
=
[]
children
=
[]
for
child
in
xml_object
:
for
child
in
xml_object
:
try
:
try
:
children
.
append
(
system
.
process_xml
(
etree
.
tostring
(
child
,
encoding
=
'unicode'
))
.
location
.
url
())
child_block
=
system
.
process_xml
(
etree
.
tostring
(
child
,
encoding
=
'unicode'
))
children
.
append
(
child_block
.
scope_ids
.
usage_id
)
except
Exception
as
e
:
except
Exception
as
e
:
log
.
exception
(
"Unable to load child when parsing Sequence. Continuing..."
)
log
.
exception
(
"Unable to load child when parsing Sequence. Continuing..."
)
if
system
.
error_tracker
is
not
None
:
if
system
.
error_tracker
is
not
None
:
...
...
common/lib/xmodule/xmodule/tests/__init__.py
View file @
d2a0df41
...
@@ -16,10 +16,12 @@ from mock import Mock
...
@@ -16,10 +16,12 @@ from mock import Mock
from
path
import
path
from
path
import
path
from
xblock.field_data
import
DictFieldData
from
xblock.field_data
import
DictFieldData
from
xmodule.x_module
import
ModuleSystem
,
XModuleDescriptor
,
XModuleMixin
from
xmodule.x_module
import
ModuleSystem
,
XModuleDescriptor
,
XModuleMixin
from
xmodule.modulestore.inheritance
import
InheritanceMixin
from
xmodule.modulestore.inheritance
import
InheritanceMixin
from
xmodule.mako_module
import
MakoDescriptorSystem
from
xmodule.mako_module
import
MakoDescriptorSystem
from
xmodule.error_module
import
ErrorDescriptor
from
xmodule.error_module
import
ErrorDescriptor
from
xmodule.modulestore.xml
import
LocationReader
# Location of common test DATA directory
# Location of common test DATA directory
...
@@ -88,6 +90,8 @@ def get_test_descriptor_system():
...
@@ -88,6 +90,8 @@ def get_test_descriptor_system():
error_tracker
=
Mock
(),
error_tracker
=
Mock
(),
render_template
=
mock_render_template
,
render_template
=
mock_render_template
,
mixins
=
(
InheritanceMixin
,
XModuleMixin
),
mixins
=
(
InheritanceMixin
,
XModuleMixin
),
field_data
=
DictFieldData
({}),
id_reader
=
LocationReader
(),
)
)
...
...
common/lib/xmodule/xmodule/tests/test_conditional.py
View file @
d2a0df41
...
@@ -9,7 +9,7 @@ from xblock.field_data import DictFieldData
...
@@ -9,7 +9,7 @@ from xblock.field_data import DictFieldData
from
xblock.fields
import
ScopeIds
from
xblock.fields
import
ScopeIds
from
xmodule.error_module
import
NonStaffErrorDescriptor
from
xmodule.error_module
import
NonStaffErrorDescriptor
from
xmodule.modulestore
import
Location
from
xmodule.modulestore
import
Location
from
xmodule.modulestore.xml
import
ImportSystem
,
XMLModuleStore
from
xmodule.modulestore.xml
import
ImportSystem
,
XMLModuleStore
,
CourseLocationGenerator
from
xmodule.conditional_module
import
ConditionalDescriptor
from
xmodule.conditional_module
import
ConditionalDescriptor
from
xmodule.tests
import
DATA_DIR
,
get_test_system
,
get_test_descriptor_system
from
xmodule.tests
import
DATA_DIR
,
get_test_system
,
get_test_descriptor_system
...
@@ -32,7 +32,6 @@ class DummySystem(ImportSystem):
...
@@ -32,7 +32,6 @@ class DummySystem(ImportSystem):
error_tracker
=
Mock
(),
error_tracker
=
Mock
(),
parent_tracker
=
Mock
(),
parent_tracker
=
Mock
(),
load_error_modules
=
load_error_modules
,
load_error_modules
=
load_error_modules
,
policy
=
{},
)
)
def
render_template
(
self
,
template
,
context
):
def
render_template
(
self
,
template
,
context
):
...
@@ -61,8 +60,7 @@ class ConditionalFactory(object):
...
@@ -61,8 +60,7 @@ class ConditionalFactory(object):
source_descriptor
=
NonStaffErrorDescriptor
.
from_xml
(
source_descriptor
=
NonStaffErrorDescriptor
.
from_xml
(
'some random xml data'
,
'some random xml data'
,
system
,
system
,
org
=
source_location
.
org
,
id_generator
=
CourseLocationGenerator
(
source_location
.
org
,
source_location
.
course
),
course
=
source_location
.
course
,
error_msg
=
'random error message'
error_msg
=
'random error message'
)
)
else
:
else
:
...
...
common/lib/xmodule/xmodule/tests/test_course_module.py
View file @
d2a0df41
...
@@ -5,8 +5,10 @@ from fs.memoryfs import MemoryFS
...
@@ -5,8 +5,10 @@ from fs.memoryfs import MemoryFS
from
mock
import
Mock
,
patch
from
mock
import
Mock
,
patch
from
xmodule.modulestore.xml
import
ImportSystem
,
XMLModuleStore
from
xblock.runtime
import
KvsFieldData
,
DictKeyValueStore
import
xmodule.course_module
import
xmodule.course_module
from
xmodule.modulestore.xml
import
ImportSystem
,
XMLModuleStore
,
LocationReader
from
django.utils.timezone
import
UTC
from
django.utils.timezone
import
UTC
...
@@ -32,7 +34,6 @@ class DummySystem(ImportSystem):
...
@@ -32,7 +34,6 @@ class DummySystem(ImportSystem):
load_error_modules
=
load_error_modules
)
load_error_modules
=
load_error_modules
)
course_id
=
"/"
.
join
([
ORG
,
COURSE
,
'test_run'
])
course_id
=
"/"
.
join
([
ORG
,
COURSE
,
'test_run'
])
course_dir
=
"test_dir"
course_dir
=
"test_dir"
policy
=
{}
error_tracker
=
Mock
()
error_tracker
=
Mock
()
parent_tracker
=
Mock
()
parent_tracker
=
Mock
()
...
@@ -40,10 +41,11 @@ class DummySystem(ImportSystem):
...
@@ -40,10 +41,11 @@ class DummySystem(ImportSystem):
xmlstore
=
xmlstore
,
xmlstore
=
xmlstore
,
course_id
=
course_id
,
course_id
=
course_id
,
course_dir
=
course_dir
,
course_dir
=
course_dir
,
policy
=
policy
,
error_tracker
=
error_tracker
,
error_tracker
=
error_tracker
,
parent_tracker
=
parent_tracker
,
parent_tracker
=
parent_tracker
,
load_error_modules
=
load_error_modules
,
load_error_modules
=
load_error_modules
,
field_data
=
KvsFieldData
(
DictKeyValueStore
()),
id_reader
=
LocationReader
(),
)
)
...
...
common/lib/xmodule/xmodule/tests/test_error_module.py
View file @
d2a0df41
...
@@ -5,9 +5,10 @@ import unittest
...
@@ -5,9 +5,10 @@ import unittest
from
xmodule.tests
import
get_test_system
from
xmodule.tests
import
get_test_system
from
xmodule.error_module
import
ErrorDescriptor
,
ErrorModule
,
NonStaffErrorDescriptor
from
xmodule.error_module
import
ErrorDescriptor
,
ErrorModule
,
NonStaffErrorDescriptor
from
xmodule.modulestore
import
Location
from
xmodule.modulestore
import
Location
from
xmodule.modulestore.xml
import
CourseLocationGenerator
from
xmodule.x_module
import
XModuleDescriptor
,
XModule
from
xmodule.x_module
import
XModuleDescriptor
,
XModule
from
mock
import
MagicMock
,
Mock
,
patch
from
mock
import
MagicMock
,
Mock
,
patch
from
xblock.runtime
import
Runtime
,
UsageStore
from
xblock.runtime
import
Runtime
,
IdReader
from
xblock.field_data
import
FieldData
from
xblock.field_data
import
FieldData
from
xblock.fields
import
ScopeIds
from
xblock.fields
import
ScopeIds
from
xblock.test.tools
import
unabc
from
xblock.test.tools
import
unabc
...
@@ -32,7 +33,11 @@ class TestErrorModule(unittest.TestCase, SetupTestErrorModules):
...
@@ -32,7 +33,11 @@ class TestErrorModule(unittest.TestCase, SetupTestErrorModules):
def
test_error_module_xml_rendering
(
self
):
def
test_error_module_xml_rendering
(
self
):
descriptor
=
ErrorDescriptor
.
from_xml
(
descriptor
=
ErrorDescriptor
.
from_xml
(
self
.
valid_xml
,
self
.
system
,
self
.
org
,
self
.
course
,
self
.
error_msg
)
self
.
valid_xml
,
self
.
system
,
CourseLocationGenerator
(
self
.
org
,
self
.
course
),
self
.
error_msg
)
self
.
assertIsInstance
(
descriptor
,
ErrorDescriptor
)
self
.
assertIsInstance
(
descriptor
,
ErrorDescriptor
)
descriptor
.
xmodule_runtime
=
self
.
system
descriptor
.
xmodule_runtime
=
self
.
system
context_repr
=
self
.
system
.
render
(
descriptor
,
'student_view'
)
.
content
context_repr
=
self
.
system
.
render
(
descriptor
,
'student_view'
)
.
content
...
@@ -63,12 +68,18 @@ class TestNonStaffErrorModule(unittest.TestCase, SetupTestErrorModules):
...
@@ -63,12 +68,18 @@ class TestNonStaffErrorModule(unittest.TestCase, SetupTestErrorModules):
def
test_non_staff_error_module_create
(
self
):
def
test_non_staff_error_module_create
(
self
):
descriptor
=
NonStaffErrorDescriptor
.
from_xml
(
descriptor
=
NonStaffErrorDescriptor
.
from_xml
(
self
.
valid_xml
,
self
.
system
,
self
.
org
,
self
.
course
)
self
.
valid_xml
,
self
.
system
,
CourseLocationGenerator
(
self
.
org
,
self
.
course
)
)
self
.
assertIsInstance
(
descriptor
,
NonStaffErrorDescriptor
)
self
.
assertIsInstance
(
descriptor
,
NonStaffErrorDescriptor
)
def
test_from_xml_render
(
self
):
def
test_from_xml_render
(
self
):
descriptor
=
NonStaffErrorDescriptor
.
from_xml
(
descriptor
=
NonStaffErrorDescriptor
.
from_xml
(
self
.
valid_xml
,
self
.
system
,
self
.
org
,
self
.
course
)
self
.
valid_xml
,
self
.
system
,
CourseLocationGenerator
(
self
.
org
,
self
.
course
)
)
descriptor
.
xmodule_runtime
=
self
.
system
descriptor
.
xmodule_runtime
=
self
.
system
context_repr
=
self
.
system
.
render
(
descriptor
,
'student_view'
)
.
content
context_repr
=
self
.
system
.
render
(
descriptor
,
'student_view'
)
.
content
self
.
assertNotIn
(
self
.
error_msg
,
context_repr
)
self
.
assertNotIn
(
self
.
error_msg
,
context_repr
)
...
@@ -117,11 +128,11 @@ class TestErrorModuleConstruction(unittest.TestCase):
...
@@ -117,11 +128,11 @@ class TestErrorModuleConstruction(unittest.TestCase):
def
setUp
(
self
):
def
setUp
(
self
):
field_data
=
Mock
(
spec
=
FieldData
)
field_data
=
Mock
(
spec
=
FieldData
)
self
.
descriptor
=
BrokenDescriptor
(
self
.
descriptor
=
BrokenDescriptor
(
TestRuntime
(
Mock
(
spec
=
UsageStore
),
field_data
),
TestRuntime
(
Mock
(
spec
=
IdReader
),
field_data
),
field_data
,
field_data
,
ScopeIds
(
None
,
None
,
None
,
'i4x://org/course/broken/name'
)
ScopeIds
(
None
,
None
,
None
,
'i4x://org/course/broken/name'
)
)
)
self
.
descriptor
.
xmodule_runtime
=
TestRuntime
(
Mock
(
spec
=
UsageStore
),
field_data
)
self
.
descriptor
.
xmodule_runtime
=
TestRuntime
(
Mock
(
spec
=
IdReader
),
field_data
)
self
.
descriptor
.
xmodule_runtime
.
error_descriptor_class
=
ErrorDescriptor
self
.
descriptor
.
xmodule_runtime
.
error_descriptor_class
=
ErrorDescriptor
self
.
descriptor
.
xmodule_runtime
.
xmodule_instance
=
None
self
.
descriptor
.
xmodule_runtime
.
xmodule_instance
=
None
...
...
common/lib/xmodule/xmodule/tests/test_import.py
View file @
d2a0df41
# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-
import
datetime
import
datetime
import
ddt
import
unittest
import
unittest
from
fs.memoryfs
import
MemoryFS
from
fs.memoryfs
import
MemoryFS
...
@@ -11,13 +12,17 @@ from django.utils.timezone import UTC
...
@@ -11,13 +12,17 @@ from django.utils.timezone import UTC
from
xmodule.xml_module
import
is_pointer_tag
from
xmodule.xml_module
import
is_pointer_tag
from
xmodule.modulestore
import
Location
from
xmodule.modulestore
import
Location
from
xmodule.modulestore.xml
import
ImportSystem
,
XMLModuleStore
from
xmodule.modulestore.xml
import
ImportSystem
,
XMLModuleStore
,
LocationReader
from
xmodule.modulestore.inheritance
import
compute_inherited_metadata
from
xmodule.modulestore.inheritance
import
compute_inherited_metadata
from
xmodule.x_module
import
XModuleMixin
,
only_xmodules
from
xmodule.x_module
import
XModuleMixin
,
only_xmodules
from
xmodule.fields
import
Date
from
xmodule.fields
import
Date
from
xmodule.tests
import
DATA_DIR
from
xmodule.tests
import
DATA_DIR
from
xmodule.modulestore.inheritance
import
InheritanceMixin
from
xmodule.modulestore.inheritance
import
InheritanceMixin
from
xblock.core
import
XBlock
from
xblock.fields
import
Scope
,
String
,
Integer
from
xblock.runtime
import
KvsFieldData
,
DictKeyValueStore
ORG
=
'test_org'
ORG
=
'test_org'
COURSE
=
'test_course'
COURSE
=
'test_course'
...
@@ -31,7 +36,6 @@ class DummySystem(ImportSystem):
...
@@ -31,7 +36,6 @@ class DummySystem(ImportSystem):
xmlstore
=
XMLModuleStore
(
"data_dir"
,
course_dirs
=
[],
load_error_modules
=
load_error_modules
)
xmlstore
=
XMLModuleStore
(
"data_dir"
,
course_dirs
=
[],
load_error_modules
=
load_error_modules
)
course_id
=
"/"
.
join
([
ORG
,
COURSE
,
'test_run'
])
course_id
=
"/"
.
join
([
ORG
,
COURSE
,
'test_run'
])
course_dir
=
"test_dir"
course_dir
=
"test_dir"
policy
=
{}
error_tracker
=
Mock
()
error_tracker
=
Mock
()
parent_tracker
=
Mock
()
parent_tracker
=
Mock
()
...
@@ -39,11 +43,12 @@ class DummySystem(ImportSystem):
...
@@ -39,11 +43,12 @@ class DummySystem(ImportSystem):
xmlstore
=
xmlstore
,
xmlstore
=
xmlstore
,
course_id
=
course_id
,
course_id
=
course_id
,
course_dir
=
course_dir
,
course_dir
=
course_dir
,
policy
=
policy
,
error_tracker
=
error_tracker
,
error_tracker
=
error_tracker
,
parent_tracker
=
parent_tracker
,
parent_tracker
=
parent_tracker
,
load_error_modules
=
load_error_modules
,
load_error_modules
=
load_error_modules
,
mixins
=
(
InheritanceMixin
,
XModuleMixin
)
mixins
=
(
InheritanceMixin
,
XModuleMixin
),
field_data
=
KvsFieldData
(
DictKeyValueStore
()),
id_reader
=
LocationReader
(),
)
)
def
render_template
(
self
,
_template
,
_context
):
def
render_template
(
self
,
_template
,
_context
):
...
@@ -72,6 +77,40 @@ class BaseCourseTestCase(unittest.TestCase):
...
@@ -72,6 +77,40 @@ class BaseCourseTestCase(unittest.TestCase):
return
courses
[
0
]
return
courses
[
0
]
class
GenericXBlock
(
XBlock
):
has_children
=
True
field1
=
String
(
default
=
"something"
,
scope
=
Scope
.
user_state
)
field2
=
Integer
(
scope
=
Scope
.
user_state
)
@ddt.ddt
class
PureXBlockImportTest
(
BaseCourseTestCase
):
def
assert_xblocks_are_good
(
self
,
block
):
"""Assert a number of conditions that must be true for `block` to be good."""
scope_ids
=
block
.
scope_ids
self
.
assertIsNotNone
(
scope_ids
.
usage_id
)
self
.
assertIsNotNone
(
scope_ids
.
def_id
)
for
child_id
in
block
.
children
:
child
=
block
.
runtime
.
get_block
(
child_id
)
self
.
assert_xblocks_are_good
(
child
)
@XBlock.register_temp_plugin
(
GenericXBlock
)
@ddt.data
(
"<genericxblock/>"
,
"<genericxblock field1='abc' field2='23' />"
,
"<genericxblock field1='abc' field2='23'><genericxblock/></genericxblock>"
,
)
@patch
(
'xmodule.x_module.XModuleMixin.location'
)
def
test_parsing_pure_xblock
(
self
,
xml
,
mock_location
):
system
=
self
.
get_system
(
load_error_modules
=
False
)
descriptor
=
system
.
process_xml
(
xml
)
self
.
assertIsInstance
(
descriptor
,
GenericXBlock
)
self
.
assert_xblocks_are_good
(
descriptor
)
self
.
assertFalse
(
mock_location
.
called
)
class
ImportTestCase
(
BaseCourseTestCase
):
class
ImportTestCase
(
BaseCourseTestCase
):
date
=
Date
()
date
=
Date
()
...
@@ -119,13 +158,10 @@ class ImportTestCase(BaseCourseTestCase):
...
@@ -119,13 +158,10 @@ class ImportTestCase(BaseCourseTestCase):
tag_xml
=
descriptor
.
export_to_xml
(
resource_fs
)
tag_xml
=
descriptor
.
export_to_xml
(
resource_fs
)
re_import_descriptor
=
system
.
process_xml
(
tag_xml
)
re_import_descriptor
=
system
.
process_xml
(
tag_xml
)
self
.
assertEqual
(
re_import_descriptor
.
__class__
.
__name__
,
self
.
assertEqual
(
re_import_descriptor
.
__class__
.
__name__
,
'ErrorDescriptorWithMixins'
)
'ErrorDescriptorWithMixins'
)
self
.
assertEqual
(
descriptor
.
contents
,
self
.
assertEqual
(
descriptor
.
contents
,
re_import_descriptor
.
contents
)
re_import_descriptor
.
contents
)
self
.
assertEqual
(
descriptor
.
error_msg
,
re_import_descriptor
.
error_msg
)
self
.
assertEqual
(
descriptor
.
error_msg
,
re_import_descriptor
.
error_msg
)
def
test_fixed_xml_tag
(
self
):
def
test_fixed_xml_tag
(
self
):
"""Make sure a tag that's been fixed exports as the original tag type"""
"""Make sure a tag that's been fixed exports as the original tag type"""
...
@@ -410,7 +446,7 @@ class ImportTestCase(BaseCourseTestCase):
...
@@ -410,7 +446,7 @@ class ImportTestCase(BaseCourseTestCase):
self
.
assertTrue
(
any
(
expect
in
msg
or
expect
in
err
self
.
assertTrue
(
any
(
expect
in
msg
or
expect
in
err
for
msg
,
err
in
errors
))
for
msg
,
err
in
errors
))
chapters
=
course
.
get_children
()
chapters
=
course
.
get_children
()
self
.
assertEqual
(
len
(
chapters
),
3
)
self
.
assertEqual
(
len
(
chapters
),
4
)
def
test_url_name_mangling
(
self
):
def
test_url_name_mangling
(
self
):
"""
"""
...
...
common/lib/xmodule/xmodule/tests/test_peer_grading.py
View file @
d2a0df41
...
@@ -414,6 +414,6 @@ class PeerGradingModuleTrackChangesTest(unittest.TestCase, DummyModulestore):
...
@@ -414,6 +414,6 @@ class PeerGradingModuleTrackChangesTest(unittest.TestCase, DummyModulestore):
@return:
@return:
"""
"""
self
.
peer_grading
.
_find_corresponding_module_for_location
=
self
.
mock_track_changes_problem
self
.
peer_grading
.
_find_corresponding_module_for_location
=
self
.
mock_track_changes_problem
response
=
self
.
peer_grading
.
peer_grading_problem
({
'location'
:
'
mocked
'
})
response
=
self
.
peer_grading
.
peer_grading_problem
({
'location'
:
'
i4x://mock_org/mock_course/mock_cat/mock_name
'
})
self
.
assertTrue
(
response
[
'success'
])
self
.
assertTrue
(
response
[
'success'
])
self
.
assertIn
(
"'track_changes': True"
,
response
[
'html'
])
self
.
assertIn
(
"'track_changes': True"
,
response
[
'html'
])
common/lib/xmodule/xmodule/tests/test_video.py
View file @
d2a0df41
...
@@ -114,9 +114,10 @@ class VideoDescriptorTest(unittest.TestCase):
...
@@ -114,9 +114,10 @@ class VideoDescriptorTest(unittest.TestCase):
def
setUp
(
self
):
def
setUp
(
self
):
system
=
get_test_descriptor_system
()
system
=
get_test_descriptor_system
()
location
=
Location
(
'i4x://org/course/video/name'
)
self
.
descriptor
=
system
.
construct_xblock_from_class
(
self
.
descriptor
=
system
.
construct_xblock_from_class
(
VideoDescriptor
,
VideoDescriptor
,
scope_ids
=
ScopeIds
(
None
,
None
,
None
,
None
),
scope_ids
=
ScopeIds
(
None
,
None
,
location
,
location
),
field_data
=
DictFieldData
({}),
field_data
=
DictFieldData
({}),
)
)
...
@@ -226,7 +227,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
...
@@ -226,7 +227,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
<track src="http://www.example.com/track"/>
<track src="http://www.example.com/track"/>
</video>
</video>
'''
'''
output
=
VideoDescriptor
.
from_xml
(
xml_data
,
module_system
)
output
=
VideoDescriptor
.
from_xml
(
xml_data
,
module_system
,
Mock
()
)
self
.
assert_attributes_equal
(
output
,
{
self
.
assert_attributes_equal
(
output
,
{
'youtube_id_0_75'
:
'izygArpw-Qo'
,
'youtube_id_0_75'
:
'izygArpw-Qo'
,
'youtube_id_1_0'
:
'p2Q6BrNhdh8'
,
'youtube_id_1_0'
:
'p2Q6BrNhdh8'
,
...
@@ -255,7 +256,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
...
@@ -255,7 +256,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
<track src="http://www.example.com/track"/>
<track src="http://www.example.com/track"/>
</video>
</video>
'''
'''
output
=
VideoDescriptor
.
from_xml
(
xml_data
,
module_system
)
output
=
VideoDescriptor
.
from_xml
(
xml_data
,
module_system
,
Mock
()
)
self
.
assert_attributes_equal
(
output
,
{
self
.
assert_attributes_equal
(
output
,
{
'youtube_id_0_75'
:
''
,
'youtube_id_0_75'
:
''
,
'youtube_id_1_0'
:
'p2Q6BrNhdh8'
,
'youtube_id_1_0'
:
'p2Q6BrNhdh8'
,
...
@@ -276,7 +277,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
...
@@ -276,7 +277,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
"""
"""
module_system
=
DummySystem
(
load_error_modules
=
True
)
module_system
=
DummySystem
(
load_error_modules
=
True
)
xml_data
=
'<video></video>'
xml_data
=
'<video></video>'
output
=
VideoDescriptor
.
from_xml
(
xml_data
,
module_system
)
output
=
VideoDescriptor
.
from_xml
(
xml_data
,
module_system
,
Mock
()
)
self
.
assert_attributes_equal
(
output
,
{
self
.
assert_attributes_equal
(
output
,
{
'youtube_id_0_75'
:
''
,
'youtube_id_0_75'
:
''
,
'youtube_id_1_0'
:
'OEoXaMPEzfM'
,
'youtube_id_1_0'
:
'OEoXaMPEzfM'
,
...
@@ -310,7 +311,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
...
@@ -310,7 +311,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
youtube_id_1_0=""OEoXaMPEzf10""
youtube_id_1_0=""OEoXaMPEzf10""
/>
/>
'''
'''
output
=
VideoDescriptor
.
from_xml
(
xml_data
,
module_system
)
output
=
VideoDescriptor
.
from_xml
(
xml_data
,
module_system
,
Mock
()
)
self
.
assert_attributes_equal
(
output
,
{
self
.
assert_attributes_equal
(
output
,
{
'youtube_id_0_75'
:
'OEoXaMPEzf65'
,
'youtube_id_0_75'
:
'OEoXaMPEzf65'
,
'youtube_id_1_0'
:
'OEoXaMPEzf10'
,
'youtube_id_1_0'
:
'OEoXaMPEzf10'
,
...
@@ -332,7 +333,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
...
@@ -332,7 +333,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
youtube="1.0:"p2Q6BrNhdh8",1.25:"1EeWXzPdhSA"">
youtube="1.0:"p2Q6BrNhdh8",1.25:"1EeWXzPdhSA"">
</video>
</video>
'''
'''
output
=
VideoDescriptor
.
from_xml
(
xml_data
,
module_system
)
output
=
VideoDescriptor
.
from_xml
(
xml_data
,
module_system
,
Mock
()
)
self
.
assert_attributes_equal
(
output
,
{
self
.
assert_attributes_equal
(
output
,
{
'youtube_id_0_75'
:
''
,
'youtube_id_0_75'
:
''
,
'youtube_id_1_0'
:
'p2Q6BrNhdh8'
,
'youtube_id_1_0'
:
'p2Q6BrNhdh8'
,
...
@@ -362,7 +363,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
...
@@ -362,7 +363,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
<track src="http://www.example.com/track"/>
<track src="http://www.example.com/track"/>
</video>
</video>
"""
"""
output
=
VideoDescriptor
.
from_xml
(
xml_data
,
module_system
)
output
=
VideoDescriptor
.
from_xml
(
xml_data
,
module_system
,
Mock
()
)
self
.
assert_attributes_equal
(
output
,
{
self
.
assert_attributes_equal
(
output
,
{
'youtube_id_0_75'
:
'izygArpw-Qo'
,
'youtube_id_0_75'
:
'izygArpw-Qo'
,
'youtube_id_1_0'
:
'p2Q6BrNhdh8'
,
'youtube_id_1_0'
:
'p2Q6BrNhdh8'
,
...
@@ -391,7 +392,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
...
@@ -391,7 +392,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
<track src="http://www.example.com/track"/>
<track src="http://www.example.com/track"/>
</video>
</video>
"""
"""
video
=
VideoDescriptor
.
from_xml
(
xml_data
,
module_system
)
video
=
VideoDescriptor
.
from_xml
(
xml_data
,
module_system
,
Mock
()
)
self
.
assert_attributes_equal
(
video
,
{
self
.
assert_attributes_equal
(
video
,
{
'youtube_id_0_75'
:
'izygArpw-Qo'
,
'youtube_id_0_75'
:
'izygArpw-Qo'
,
'youtube_id_1_0'
:
'p2Q6BrNhdh8'
,
'youtube_id_1_0'
:
'p2Q6BrNhdh8'
,
...
@@ -420,7 +421,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
...
@@ -420,7 +421,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
<track src="http://www.example.com/track"/>
<track src="http://www.example.com/track"/>
</video>
</video>
"""
"""
video
=
VideoDescriptor
.
from_xml
(
xml_data
,
module_system
)
video
=
VideoDescriptor
.
from_xml
(
xml_data
,
module_system
,
Mock
()
)
self
.
assert_attributes_equal
(
video
,
{
self
.
assert_attributes_equal
(
video
,
{
'youtube_id_0_75'
:
'izygArpw-Qo'
,
'youtube_id_0_75'
:
'izygArpw-Qo'
,
'youtube_id_1_0'
:
'p2Q6BrNhdh8'
,
'youtube_id_1_0'
:
'p2Q6BrNhdh8'
,
...
...
common/lib/xmodule/xmodule/tests/test_xblock_wrappers.py
View file @
d2a0df41
...
@@ -13,6 +13,8 @@ from mock import Mock
...
@@ -13,6 +13,8 @@ from mock import Mock
from
xblock.field_data
import
DictFieldData
from
xblock.field_data
import
DictFieldData
from
xblock.fields
import
ScopeIds
from
xblock.fields
import
ScopeIds
from
xmodule.modulestore
import
Location
from
xmodule.x_module
import
ModuleSystem
,
XModule
,
XModuleDescriptor
from
xmodule.x_module
import
ModuleSystem
,
XModule
,
XModuleDescriptor
from
xmodule.mako_module
import
MakoDescriptorSystem
from
xmodule.mako_module
import
MakoDescriptorSystem
from
xmodule.annotatable_module
import
AnnotatableDescriptor
from
xmodule.annotatable_module
import
AnnotatableDescriptor
...
@@ -75,7 +77,7 @@ class TestXBlockWrapper(object):
...
@@ -75,7 +77,7 @@ class TestXBlockWrapper(object):
return
get_test_system
()
return
get_test_system
()
def
leaf_descriptor
(
self
,
descriptor_cls
):
def
leaf_descriptor
(
self
,
descriptor_cls
):
location
=
'i4x://org/course/category/name'
location
=
Location
(
'i4x://org/course/category/name'
)
runtime
=
get_test_descriptor_system
()
runtime
=
get_test_descriptor_system
()
return
runtime
.
construct_xblock_from_class
(
return
runtime
.
construct_xblock_from_class
(
descriptor_cls
,
descriptor_cls
,
...
@@ -100,7 +102,7 @@ class TestXBlockWrapper(object):
...
@@ -100,7 +102,7 @@ class TestXBlockWrapper(object):
def
container_descriptor
(
self
,
descriptor_cls
,
depth
):
def
container_descriptor
(
self
,
descriptor_cls
,
depth
):
"""Return an instance of `descriptor_cls` with `depth` levels of children"""
"""Return an instance of `descriptor_cls` with `depth` levels of children"""
location
=
'i4x://org/course/category/name'
location
=
Location
(
'i4x://org/course/category/name'
)
runtime
=
get_test_descriptor_system
()
runtime
=
get_test_descriptor_system
()
if
depth
==
0
:
if
depth
==
0
:
...
...
common/lib/xmodule/xmodule/tests/test_xml_module.py
View file @
d2a0df41
...
@@ -8,10 +8,10 @@ from nose.tools import assert_equals, assert_not_equals, assert_true, assert_fal
...
@@ -8,10 +8,10 @@ from nose.tools import assert_equals, assert_not_equals, assert_true, assert_fal
from
xblock.field_data
import
DictFieldData
from
xblock.field_data
import
DictFieldData
from
xblock.fields
import
Scope
,
String
,
Dict
,
Boolean
,
Integer
,
Float
,
Any
,
List
from
xblock.fields
import
Scope
,
String
,
Dict
,
Boolean
,
Integer
,
Float
,
Any
,
List
from
xblock.runtime
import
DbModel
from
xblock.runtime
import
KvsFieldData
,
DictKeyValueStore
from
xmodule.fields
import
Date
,
Timedelta
,
RelativeTime
from
xmodule.fields
import
Date
,
Timedelta
,
RelativeTime
from
xmodule.modulestore.inheritance
import
InheritanceKeyValueStore
,
InheritanceMixin
from
xmodule.modulestore.inheritance
import
InheritanceKeyValueStore
,
InheritanceMixin
,
InheritingFieldData
from
xmodule.xml_module
import
XmlDescriptor
,
serialize_field
,
deserialize_field
from
xmodule.xml_module
import
XmlDescriptor
,
serialize_field
,
deserialize_field
from
xmodule.course_module
import
CourseDescriptor
from
xmodule.course_module
import
CourseDescriptor
from
xmodule.seq_module
import
SequenceDescriptor
from
xmodule.seq_module
import
SequenceDescriptor
...
@@ -58,6 +58,87 @@ class TestFields(object):
...
@@ -58,6 +58,87 @@ class TestFields(object):
# Used for testing Lists
# Used for testing Lists
list_field
=
List
(
scope
=
Scope
.
settings
,
default
=
[])
list_field
=
List
(
scope
=
Scope
.
settings
,
default
=
[])
class
InheritingFieldDataTest
(
unittest
.
TestCase
):
"""Tests of InheritingFieldData."""
class
TestableInheritingXBlock
(
XmlDescriptor
):
"""An XBlock we can use in these tests."""
inherited
=
String
(
scope
=
Scope
.
settings
,
default
=
"the default"
)
not_inherited
=
String
(
scope
=
Scope
.
settings
,
default
=
"nothing"
)
def
setUp
(
self
):
self
.
system
=
get_test_descriptor_system
()
self
.
all_blocks
=
{}
self
.
system
.
get_block
=
self
.
all_blocks
.
get
self
.
field_data
=
InheritingFieldData
(
inheritable_names
=
[
'inherited'
],
kvs
=
DictKeyValueStore
({}),
)
def
get_a_block
(
self
,
usage_id
=
None
):
"""Construct an XBlock for testing with."""
scope_ids
=
Mock
()
if
usage_id
is
None
:
usage_id
=
"_auto
%
d"
%
len
(
self
.
all_blocks
)
scope_ids
.
usage_id
=
usage_id
block
=
self
.
system
.
construct_xblock_from_class
(
self
.
TestableInheritingXBlock
,
field_data
=
self
.
field_data
,
scope_ids
=
scope_ids
,
)
self
.
all_blocks
[
usage_id
]
=
block
return
block
def
test_default_value
(
self
):
# Blocks with nothing set with return the fields' defaults.
block
=
self
.
get_a_block
()
self
.
assertEqual
(
block
.
inherited
,
"the default"
)
self
.
assertEqual
(
block
.
not_inherited
,
"nothing"
)
def
test_set_value
(
self
):
# If you set a value, that's what you get back.
block
=
self
.
get_a_block
()
block
.
inherited
=
"Changed!"
block
.
not_inherited
=
"New Value!"
self
.
assertEqual
(
block
.
inherited
,
"Changed!"
)
self
.
assertEqual
(
block
.
not_inherited
,
"New Value!"
)
def
test_inherited
(
self
):
# A child with get a value inherited from the parent.
parent
=
self
.
get_a_block
(
usage_id
=
"parent"
)
parent
.
inherited
=
"Changed!"
self
.
assertEqual
(
parent
.
inherited
,
"Changed!"
)
child
=
self
.
get_a_block
(
usage_id
=
"child"
)
child
.
parent
=
"parent"
self
.
assertEqual
(
child
.
inherited
,
"Changed!"
)
def
test_inherited_across_generations
(
self
):
# A child with get a value inherited from a great-grandparent.
parent
=
self
.
get_a_block
(
usage_id
=
"parent"
)
parent
.
inherited
=
"Changed!"
self
.
assertEqual
(
parent
.
inherited
,
"Changed!"
)
parent_id
=
"parent"
for
child_num
in
range
(
10
):
usage_id
=
"child_{}"
.
format
(
child_num
)
child
=
self
.
get_a_block
(
usage_id
=
usage_id
)
child
.
parent
=
"parent"
self
.
assertEqual
(
child
.
inherited
,
"Changed!"
)
parent_id
=
usage_id
def
test_not_inherited
(
self
):
# Fields not in the inherited_names list won't be inherited.
parent
=
self
.
get_a_block
(
usage_id
=
"parent"
)
parent
.
not_inherited
=
"Changed!"
self
.
assertEqual
(
parent
.
not_inherited
,
"Changed!"
)
child
=
self
.
get_a_block
(
usage_id
=
"child"
)
child
.
parent
=
"parent"
self
.
assertEqual
(
child
.
not_inherited
,
"nothing"
)
class
EditableMetadataFieldsTest
(
unittest
.
TestCase
):
class
EditableMetadataFieldsTest
(
unittest
.
TestCase
):
def
test_display_name_field
(
self
):
def
test_display_name_field
(
self
):
editable_fields
=
self
.
get_xml_editable_fields
(
DictFieldData
({}))
editable_fields
=
self
.
get_xml_editable_fields
(
DictFieldData
({}))
...
@@ -100,7 +181,7 @@ class EditableMetadataFieldsTest(unittest.TestCase):
...
@@ -100,7 +181,7 @@ class EditableMetadataFieldsTest(unittest.TestCase):
def
test_inherited_field
(
self
):
def
test_inherited_field
(
self
):
kvs
=
InheritanceKeyValueStore
(
initial_values
=
{},
inherited_settings
=
{
'showanswer'
:
'inherited'
})
kvs
=
InheritanceKeyValueStore
(
initial_values
=
{},
inherited_settings
=
{
'showanswer'
:
'inherited'
})
model_data
=
DbModel
(
kvs
)
model_data
=
KvsFieldData
(
kvs
)
descriptor
=
self
.
get_descriptor
(
model_data
)
descriptor
=
self
.
get_descriptor
(
model_data
)
editable_fields
=
descriptor
.
editable_metadata_fields
editable_fields
=
descriptor
.
editable_metadata_fields
self
.
assert_field_values
(
self
.
assert_field_values
(
...
@@ -113,7 +194,7 @@ class EditableMetadataFieldsTest(unittest.TestCase):
...
@@ -113,7 +194,7 @@ class EditableMetadataFieldsTest(unittest.TestCase):
initial_values
=
{
'showanswer'
:
'explicit'
},
initial_values
=
{
'showanswer'
:
'explicit'
},
inherited_settings
=
{
'showanswer'
:
'inheritable value'
}
inherited_settings
=
{
'showanswer'
:
'inheritable value'
}
)
)
model_data
=
DbModel
(
kvs
)
model_data
=
KvsFieldData
(
kvs
)
descriptor
=
self
.
get_descriptor
(
model_data
)
descriptor
=
self
.
get_descriptor
(
model_data
)
editable_fields
=
descriptor
.
editable_metadata_fields
editable_fields
=
descriptor
.
editable_metadata_fields
self
.
assert_field_values
(
self
.
assert_field_values
(
...
...
common/lib/xmodule/xmodule/tests/xml/__init__.py
View file @
d2a0df41
...
@@ -4,9 +4,12 @@ Xml parsing tests for XModules
...
@@ -4,9 +4,12 @@ Xml parsing tests for XModules
import
pprint
import
pprint
from
mock
import
Mock
from
mock
import
Mock
from
xmodule.x_module
import
XMLParsingSystem
from
xmodule.x_module
import
XMLParsingSystem
,
policy_key
from
xmodule.mako_module
import
MakoDescriptorSystem
from
xmodule.mako_module
import
MakoDescriptorSystem
from
xmodule.modulestore.xml
import
create_block_from_xml
from
xmodule.modulestore.xml
import
create_block_from_xml
,
LocationReader
,
CourseLocationGenerator
from
xmodule.modulestore
import
Location
from
xblock.runtime
import
KvsFieldData
,
DictKeyValueStore
class
InMemorySystem
(
XMLParsingSystem
,
MakoDescriptorSystem
):
# pylint: disable=abstract-method
class
InMemorySystem
(
XMLParsingSystem
,
MakoDescriptorSystem
):
# pylint: disable=abstract-method
...
@@ -18,26 +21,37 @@ class InMemorySystem(XMLParsingSystem, MakoDescriptorSystem): # pylint: disable
...
@@ -18,26 +21,37 @@ class InMemorySystem(XMLParsingSystem, MakoDescriptorSystem): # pylint: disable
self
.
course
=
xml_import_data
.
course
self
.
course
=
xml_import_data
.
course
self
.
default_class
=
xml_import_data
.
default_class
self
.
default_class
=
xml_import_data
.
default_class
self
.
_descriptors
=
{}
self
.
_descriptors
=
{}
def
get_policy
(
usage_id
):
"""Return the policy data for the specified usage"""
return
xml_import_data
.
policy
.
get
(
policy_key
(
usage_id
),
{})
super
(
InMemorySystem
,
self
)
.
__init__
(
super
(
InMemorySystem
,
self
)
.
__init__
(
policy
=
xml_import_data
.
policy
,
get_policy
=
get_
policy
,
process_xml
=
self
.
process_xml
,
process_xml
=
self
.
process_xml
,
load_item
=
self
.
load_item
,
load_item
=
self
.
load_item
,
error_tracker
=
Mock
(),
error_tracker
=
Mock
(),
resources_fs
=
xml_import_data
.
filesystem
,
resources_fs
=
xml_import_data
.
filesystem
,
mixins
=
xml_import_data
.
xblock_mixins
,
mixins
=
xml_import_data
.
xblock_mixins
,
select
=
xml_import_data
.
xblock_select
,
select
=
xml_import_data
.
xblock_select
,
render_template
=
lambda
template
,
context
:
pprint
.
pformat
((
template
,
context
))
render_template
=
lambda
template
,
context
:
pprint
.
pformat
((
template
,
context
)),
field_data
=
KvsFieldData
(
DictKeyValueStore
()),
id_reader
=
LocationReader
(),
)
)
def
process_xml
(
self
,
xml
):
# pylint: disable=method-hidden
def
process_xml
(
self
,
xml
):
# pylint: disable=method-hidden
"""Parse `xml` as an XBlock, and add it to `self._descriptors`"""
"""Parse `xml` as an XBlock, and add it to `self._descriptors`"""
descriptor
=
create_block_from_xml
(
xml
,
self
,
self
.
org
,
self
.
course
,
self
.
default_class
)
descriptor
=
create_block_from_xml
(
xml
,
self
,
CourseLocationGenerator
(
self
.
org
,
self
.
course
),
)
self
.
_descriptors
[
descriptor
.
location
.
url
()]
=
descriptor
self
.
_descriptors
[
descriptor
.
location
.
url
()]
=
descriptor
return
descriptor
return
descriptor
def
load_item
(
self
,
location
):
# pylint: disable=method-hidden
def
load_item
(
self
,
location
):
# pylint: disable=method-hidden
"""Return the descriptor loaded for `location`"""
"""Return the descriptor loaded for `location`"""
return
self
.
_descriptors
[
location
]
return
self
.
_descriptors
[
Location
(
location
)
.
url
()
]
class
XModuleXmlImportTest
(
object
):
class
XModuleXmlImportTest
(
object
):
...
...
common/lib/xmodule/xmodule/video_module.py
View file @
d2a0df41
...
@@ -16,7 +16,6 @@ import logging
...
@@ -16,7 +16,6 @@ import logging
from
lxml
import
etree
from
lxml
import
etree
from
pkg_resources
import
resource_string
from
pkg_resources
import
resource_string
import
datetime
import
datetime
import
time
import
copy
import
copy
from
django.http
import
Http404
from
django.http
import
Http404
...
@@ -31,7 +30,8 @@ from xblock.fields import Scope, String, Boolean, List, Integer, ScopeIds
...
@@ -31,7 +30,8 @@ from xblock.fields import Scope, String, Boolean, List, Integer, ScopeIds
from
xmodule.fields
import
RelativeTime
from
xmodule.fields
import
RelativeTime
from
xmodule.modulestore.inheritance
import
InheritanceKeyValueStore
from
xmodule.modulestore.inheritance
import
InheritanceKeyValueStore
from
xblock.runtime
import
DbModel
from
xblock.runtime
import
KvsFieldData
log
=
logging
.
getLogger
(
__name__
)
log
=
logging
.
getLogger
(
__name__
)
...
@@ -214,7 +214,7 @@ class VideoDescriptor(VideoFields, TabsEditingDescriptor, EmptyDataRawDescriptor
...
@@ -214,7 +214,7 @@ class VideoDescriptor(VideoFields, TabsEditingDescriptor, EmptyDataRawDescriptor
del
self
.
data
del
self
.
data
@classmethod
@classmethod
def
from_xml
(
cls
,
xml_data
,
system
,
org
=
None
,
course
=
None
):
def
from_xml
(
cls
,
xml_data
,
system
,
id_generator
):
"""
"""
Creates an instance of this descriptor from the supplied xml_data.
Creates an instance of this descriptor from the supplied xml_data.
This may be overridden by subclasses
This may be overridden by subclasses
...
@@ -227,22 +227,21 @@ class VideoDescriptor(VideoFields, TabsEditingDescriptor, EmptyDataRawDescriptor
...
@@ -227,22 +227,21 @@ class VideoDescriptor(VideoFields, TabsEditingDescriptor, EmptyDataRawDescriptor
"""
"""
xml_object
=
etree
.
fromstring
(
xml_data
)
xml_object
=
etree
.
fromstring
(
xml_data
)
url_name
=
xml_object
.
get
(
'url_name'
,
xml_object
.
get
(
'slug'
))
url_name
=
xml_object
.
get
(
'url_name'
,
xml_object
.
get
(
'slug'
))
location
=
Location
(
block_type
=
'video'
'i4x'
,
org
,
course
,
'video'
,
url_name
definition_id
=
id_generator
.
create_definition
(
block_type
,
url_name
)
)
usage_id
=
id_generator
.
create_usage
(
definition_id
)
if
is_pointer_tag
(
xml_object
):
if
is_pointer_tag
(
xml_object
):
filepath
=
cls
.
_format_filepath
(
xml_object
.
tag
,
name_to_pathname
(
url_name
))
filepath
=
cls
.
_format_filepath
(
xml_object
.
tag
,
name_to_pathname
(
url_name
))
xml_data
=
etree
.
tostring
(
cls
.
load_file
(
filepath
,
system
.
resources_fs
,
location
))
xml_data
=
etree
.
tostring
(
cls
.
load_file
(
filepath
,
system
.
resources_fs
,
usage_id
))
field_data
=
cls
.
_parse_video_xml
(
xml_data
)
field_data
=
cls
.
_parse_video_xml
(
xml_data
)
field_data
[
'location'
]
=
location
kvs
=
InheritanceKeyValueStore
(
initial_values
=
field_data
)
kvs
=
InheritanceKeyValueStore
(
initial_values
=
field_data
)
field_data
=
DbModel
(
kvs
)
field_data
=
KvsFieldData
(
kvs
)
video
=
system
.
construct_xblock_from_class
(
video
=
system
.
construct_xblock_from_class
(
cls
,
cls
,
# We're loading a descriptor, so student_id is meaningless
# We're loading a descriptor, so student_id is meaningless
# We also don't have separate notions of definition and usage ids yet,
# We also don't have separate notions of definition and usage ids yet,
# so we use the location for both
# so we use the location for both
ScopeIds
(
None
,
location
.
category
,
location
,
location
),
ScopeIds
(
None
,
block_type
,
definition_id
,
usage_id
),
field_data
,
field_data
,
)
)
return
video
return
video
...
...
common/lib/xmodule/xmodule/x_module.py
View file @
d2a0df41
...
@@ -10,18 +10,19 @@ from pkg_resources import resource_listdir, resource_string, resource_isdir
...
@@ -10,18 +10,19 @@ from pkg_resources import resource_listdir, resource_string, resource_isdir
from
webob
import
Response
from
webob
import
Response
from
webob.multidict
import
MultiDict
from
webob.multidict
import
MultiDict
from
xmodule.modulestore
import
Location
from
xmodule.modulestore.exceptions
import
ItemNotFoundError
,
InsufficientSpecificationError
,
InvalidLocationError
from
xblock.core
import
XBlock
from
xblock.core
import
XBlock
from
xblock.fields
import
Scope
,
Integer
,
Float
,
List
,
XBlockMixin
,
String
from
xblock.fields
import
Scope
,
Integer
,
Float
,
List
,
XBlockMixin
,
String
from
xblock.fragment
import
Fragment
from
xblock.fragment
import
Fragment
from
xblock.plugin
import
default_select
from
xblock.plugin
import
default_select
from
xblock.runtime
import
Runtime
from
xblock.runtime
import
Runtime
,
MemoryIdManager
from
xmodule.fields
import
RelativeTime
from
xmodule.fields
import
RelativeTime
from
xmodule.errortracker
import
exc_info_to_str
from
xmodule.errortracker
import
exc_info_to_str
from
xmodule.modulestore
import
Location
from
xmodule.modulestore.exceptions
import
ItemNotFoundError
,
InsufficientSpecificationError
,
InvalidLocationError
from
xmodule.modulestore.locator
import
BlockUsageLocator
from
xmodule.modulestore.locator
import
BlockUsageLocator
log
=
logging
.
getLogger
(
__name__
)
log
=
logging
.
getLogger
(
__name__
)
...
@@ -650,28 +651,30 @@ class XModuleDescriptor(XModuleMixin, HTMLSnippet, ResourceTemplates, XBlock):
...
@@ -650,28 +651,30 @@ class XModuleDescriptor(XModuleMixin, HTMLSnippet, ResourceTemplates, XBlock):
# ================================= XML PARSING ============================
# ================================= XML PARSING ============================
@classmethod
@classmethod
def
parse_xml
(
cls
,
node
,
runtime
,
keys
):
def
parse_xml
(
cls
,
node
,
runtime
,
keys
,
id_generator
):
"""
"""
Interpret the parsed XML in `node`, creating an XModuleDescriptor.
Interpret the parsed XML in `node`, creating an XModuleDescriptor.
"""
"""
xml
=
etree
.
tostring
(
node
)
xml
=
etree
.
tostring
(
node
)
# TODO: change from_xml to not take org and course, it can use self.system.
# TODO: change from_xml to not take org and course, it can use self.system.
block
=
cls
.
from_xml
(
xml
,
runtime
,
runtime
.
org
,
runtime
.
course
)
block
=
cls
.
from_xml
(
xml
,
runtime
,
id_generator
)
return
block
return
block
@classmethod
@classmethod
def
from_xml
(
cls
,
xml_data
,
system
,
org
=
None
,
course
=
None
):
def
from_xml
(
cls
,
xml_data
,
system
,
id_generator
):
"""
"""
Creates an instance of this descriptor from the supplied xml_data.
Creates an instance of this descriptor from the supplied xml_data.
This may be overridden by subclasses.
This may be overridden by subclasses.
xml_data: A string of xml that will be translated into data and children
Args:
xml_data (str): A string of xml that will be translated into data and children
for this module
for this module
system is an XMLParsingSystem
system (:class:`.XMLParsingSystem):
id_generator (:class:`xblock.runtime.IdGenerator`): Used to generate the
usage_ids and definition_ids when loading this xml
org and course are optional strings that will be used in the generated
module's url identifiers
"""
"""
raise
NotImplementedError
(
'Modules must implement from_xml to be parsable from xml'
)
raise
NotImplementedError
(
'Modules must implement from_xml to be parsable from xml'
)
...
@@ -872,7 +875,9 @@ class DescriptorSystem(ConfigurableFragmentWrapper, Runtime): # pylint: disable
...
@@ -872,7 +875,9 @@ class DescriptorSystem(ConfigurableFragmentWrapper, Runtime): # pylint: disable
Base class for :class:`Runtime`s to be used with :class:`XModuleDescriptor`s
Base class for :class:`Runtime`s to be used with :class:`XModuleDescriptor`s
"""
"""
def
__init__
(
self
,
load_item
,
resources_fs
,
error_tracker
,
**
kwargs
):
def
__init__
(
self
,
load_item
,
resources_fs
,
error_tracker
,
get_policy
=
None
,
**
kwargs
):
"""
"""
load_item: Takes a Location and returns an XModuleDescriptor
load_item: Takes a Location and returns an XModuleDescriptor
...
@@ -907,15 +912,20 @@ class DescriptorSystem(ConfigurableFragmentWrapper, Runtime): # pylint: disable
...
@@ -907,15 +912,20 @@ class DescriptorSystem(ConfigurableFragmentWrapper, Runtime): # pylint: disable
NOTE: To avoid duplication, do not call the tracker on errors
NOTE: To avoid duplication, do not call the tracker on errors
that you're about to re-raise---let the caller track them.
that you're about to re-raise---let the caller track them.
"""
# Right now, usage_store is unused, and field_data is always supplanted
get_policy: a function that takes a usage id and returns a dict of
# with an explicit field_data during construct_xblock, so None's suffice.
policy to apply.
super
(
DescriptorSystem
,
self
)
.
__init__
(
usage_store
=
None
,
field_data
=
None
,
**
kwargs
)
"""
super
(
DescriptorSystem
,
self
)
.
__init__
(
**
kwargs
)
self
.
load_item
=
load_item
self
.
load_item
=
load_item
self
.
resources_fs
=
resources_fs
self
.
resources_fs
=
resources_fs
self
.
error_tracker
=
error_tracker
self
.
error_tracker
=
error_tracker
if
get_policy
:
self
.
get_policy
=
get_policy
else
:
self
.
get_policy
=
lambda
u
:
{}
def
get_block
(
self
,
usage_id
):
def
get_block
(
self
,
usage_id
):
"""See documentation for `xblock.runtime:Runtime.get_block`"""
"""See documentation for `xblock.runtime:Runtime.get_block`"""
...
@@ -978,17 +988,14 @@ class DescriptorSystem(ConfigurableFragmentWrapper, Runtime): # pylint: disable
...
@@ -978,17 +988,14 @@ class DescriptorSystem(ConfigurableFragmentWrapper, Runtime): # pylint: disable
class
XMLParsingSystem
(
DescriptorSystem
):
class
XMLParsingSystem
(
DescriptorSystem
):
def
__init__
(
self
,
process_xml
,
policy
,
**
kwargs
):
def
__init__
(
self
,
process_xml
,
**
kwargs
):
"""
"""
policy: a policy dictionary for overriding xml metadata
process_xml: Takes an xml string, and returns a XModuleDescriptor
process_xml: Takes an xml string, and returns a XModuleDescriptor
created from that xml
created from that xml
"""
"""
super
(
XMLParsingSystem
,
self
)
.
__init__
(
**
kwargs
)
super
(
XMLParsingSystem
,
self
)
.
__init__
(
**
kwargs
)
self
.
process_xml
=
process_xml
self
.
process_xml
=
process_xml
self
.
policy
=
policy
class
ModuleSystem
(
ConfigurableFragmentWrapper
,
Runtime
):
# pylint: disable=abstract-method
class
ModuleSystem
(
ConfigurableFragmentWrapper
,
Runtime
):
# pylint: disable=abstract-method
...
@@ -1010,7 +1017,9 @@ class ModuleSystem(ConfigurableFragmentWrapper, Runtime): # pylint: disable=abs
...
@@ -1010,7 +1017,9 @@ class ModuleSystem(ConfigurableFragmentWrapper, Runtime): # pylint: disable=abs
anonymous_student_id
=
''
,
course_id
=
None
,
anonymous_student_id
=
''
,
course_id
=
None
,
open_ended_grading_interface
=
None
,
s3_interface
=
None
,
open_ended_grading_interface
=
None
,
s3_interface
=
None
,
cache
=
None
,
can_execute_unsafe_code
=
None
,
replace_course_urls
=
None
,
cache
=
None
,
can_execute_unsafe_code
=
None
,
replace_course_urls
=
None
,
replace_jump_to_id_urls
=
None
,
error_descriptor_class
=
None
,
get_real_user
=
None
,
**
kwargs
):
replace_jump_to_id_urls
=
None
,
error_descriptor_class
=
None
,
get_real_user
=
None
,
field_data
=
None
,
**
kwargs
):
"""
"""
Create a closure around the system environment.
Create a closure around the system environment.
...
@@ -1062,11 +1071,12 @@ class ModuleSystem(ConfigurableFragmentWrapper, Runtime): # pylint: disable=abs
...
@@ -1062,11 +1071,12 @@ class ModuleSystem(ConfigurableFragmentWrapper, Runtime): # pylint: disable=abs
get_real_user - function that takes `anonymous_student_id` and returns real user_id,
get_real_user - function that takes `anonymous_student_id` and returns real user_id,
associated with `anonymous_student_id`.
associated with `anonymous_student_id`.
field_data - the `FieldData` to use for backing XBlock storage.
"""
"""
#
Right now, usage_store is unused, and field_data is always supplanted
#
Usage_store is unused, and field_data is often supplanted with an
#
with an explicit field_data during construct_xblock, so None's suffice
.
#
explicit field_data during construct_xblock
.
super
(
ModuleSystem
,
self
)
.
__init__
(
usage_store
=
None
,
field_data
=
None
,
**
kwargs
)
super
(
ModuleSystem
,
self
)
.
__init__
(
id_reader
=
None
,
field_data
=
field_data
,
**
kwargs
)
self
.
STATIC_URL
=
static_url
self
.
STATIC_URL
=
static_url
self
.
xqueue
=
xqueue
self
.
xqueue
=
xqueue
...
...
common/lib/xmodule/xmodule/xml_module.py
View file @
d2a0df41
...
@@ -6,11 +6,11 @@ import sys
...
@@ -6,11 +6,11 @@ import sys
from
lxml
import
etree
from
lxml
import
etree
from
xblock.fields
import
Dict
,
Scope
,
ScopeIds
from
xblock.fields
import
Dict
,
Scope
,
ScopeIds
from
xmodule.x_module
import
(
XModuleDescriptor
,
policy_key
)
from
xmodule.x_module
import
XModuleDescriptor
,
policy_key
from
xmodule.modulestore
import
Location
from
xmodule.modulestore
import
Location
from
xmodule.modulestore.inheritance
import
own_metadata
,
InheritanceKeyValueStore
from
xmodule.modulestore.inheritance
import
own_metadata
,
InheritanceKeyValueStore
from
xmodule.modulestore.xml_exporter
import
EdxJSONEncoder
from
xmodule.modulestore.xml_exporter
import
EdxJSONEncoder
from
xblock.runtime
import
DbModel
from
xblock.runtime
import
KvsFieldData
log
=
logging
.
getLogger
(
__name__
)
log
=
logging
.
getLogger
(
__name__
)
...
@@ -153,8 +153,7 @@ class XmlDescriptor(XModuleDescriptor):
...
@@ -153,8 +153,7 @@ class XmlDescriptor(XModuleDescriptor):
xml_object: An etree Element
xml_object: An etree Element
"""
"""
raise
NotImplementedError
(
raise
NotImplementedError
(
"
%
s does not implement definition_from_xml"
%
cls
.
__name__
)
"
%
s does not implement definition_from_xml"
%
cls
.
__name__
)
@classmethod
@classmethod
def
clean_metadata_from_xml
(
cls
,
xml_object
):
def
clean_metadata_from_xml
(
cls
,
xml_object
):
...
@@ -177,7 +176,7 @@ class XmlDescriptor(XModuleDescriptor):
...
@@ -177,7 +176,7 @@ class XmlDescriptor(XModuleDescriptor):
return
etree
.
parse
(
file_object
,
parser
=
edx_xml_parser
)
.
getroot
()
return
etree
.
parse
(
file_object
,
parser
=
edx_xml_parser
)
.
getroot
()
@classmethod
@classmethod
def
load_file
(
cls
,
filepath
,
fs
,
location
):
def
load_file
(
cls
,
filepath
,
fs
,
def_id
):
'''
'''
Open the specified file in fs, and call cls.file_to_xml on it,
Open the specified file in fs, and call cls.file_to_xml on it,
returning the lxml object.
returning the lxml object.
...
@@ -190,11 +189,11 @@ class XmlDescriptor(XModuleDescriptor):
...
@@ -190,11 +189,11 @@ class XmlDescriptor(XModuleDescriptor):
except
Exception
as
err
:
except
Exception
as
err
:
# Add info about where we are, but keep the traceback
# Add info about where we are, but keep the traceback
msg
=
'Unable to load file contents at path
%
s for item
%
s:
%
s '
%
(
msg
=
'Unable to load file contents at path
%
s for item
%
s:
%
s '
%
(
filepath
,
location
.
url
(),
str
(
err
)
)
filepath
,
def_id
,
err
)
raise
Exception
,
msg
,
sys
.
exc_info
()[
2
]
raise
Exception
,
msg
,
sys
.
exc_info
()[
2
]
@classmethod
@classmethod
def
load_definition
(
cls
,
xml_object
,
system
,
location
):
def
load_definition
(
cls
,
xml_object
,
system
,
def_id
):
'''Load a descriptor definition from the specified xml_object.
'''Load a descriptor definition from the specified xml_object.
Subclasses should not need to override this except in special
Subclasses should not need to override this except in special
cases (e.g. html module)'''
cases (e.g. html module)'''
...
@@ -220,7 +219,7 @@ class XmlDescriptor(XModuleDescriptor):
...
@@ -220,7 +219,7 @@ class XmlDescriptor(XModuleDescriptor):
filepath
=
candidate
filepath
=
candidate
break
break
definition_xml
=
cls
.
load_file
(
filepath
,
system
.
resources_fs
,
location
)
definition_xml
=
cls
.
load_file
(
filepath
,
system
.
resources_fs
,
def_id
)
definition_metadata
=
get_metadata_from_xml
(
definition_xml
)
definition_metadata
=
get_metadata_from_xml
(
definition_xml
)
cls
.
clean_metadata_from_xml
(
definition_xml
)
cls
.
clean_metadata_from_xml
(
definition_xml
)
...
@@ -269,7 +268,7 @@ class XmlDescriptor(XModuleDescriptor):
...
@@ -269,7 +268,7 @@ class XmlDescriptor(XModuleDescriptor):
metadata
[
attr
]
=
value
metadata
[
attr
]
=
value
@classmethod
@classmethod
def
from_xml
(
cls
,
xml_data
,
system
,
org
=
None
,
course
=
None
):
def
from_xml
(
cls
,
xml_data
,
system
,
id_generator
):
"""
"""
Creates an instance of this descriptor from the supplied xml_data.
Creates an instance of this descriptor from the supplied xml_data.
This may be overridden by subclasses
This may be overridden by subclasses
...
@@ -277,26 +276,25 @@ class XmlDescriptor(XModuleDescriptor):
...
@@ -277,26 +276,25 @@ class XmlDescriptor(XModuleDescriptor):
xml_data: A string of xml that will be translated into data and children for
xml_data: A string of xml that will be translated into data and children for
this module
this module
system: A DescriptorSystem for interacting with external resources
system: A DescriptorSystem for interacting with external resources
org and course are optional strings that will be used in the generated modules
url identifiers
"""
"""
xml_object
=
etree
.
fromstring
(
xml_data
)
xml_object
=
etree
.
fromstring
(
xml_data
)
# VS[compat] -- just have the url_name lookup, once translation is done
# VS[compat] -- just have the url_name lookup, once translation is done
url_name
=
xml_object
.
get
(
'url_name'
,
xml_object
.
get
(
'slug'
))
url_name
=
xml_object
.
get
(
'url_name'
,
xml_object
.
get
(
'slug'
))
location
=
Location
(
'i4x'
,
org
,
course
,
xml_object
.
tag
,
url_name
)
def_id
=
id_generator
.
create_definition
(
xml_object
.
tag
,
url_name
)
usage_id
=
id_generator
.
create_usage
(
def_id
)
# VS[compat] -- detect new-style each-in-a-file mode
# VS[compat] -- detect new-style each-in-a-file mode
if
is_pointer_tag
(
xml_object
):
if
is_pointer_tag
(
xml_object
):
# new style:
# new style:
# read the actual definition file--named using url_name.replace(':','/')
# read the actual definition file--named using url_name.replace(':','/')
filepath
=
cls
.
_format_filepath
(
xml_object
.
tag
,
name_to_pathname
(
url_name
))
filepath
=
cls
.
_format_filepath
(
xml_object
.
tag
,
name_to_pathname
(
url_name
))
definition_xml
=
cls
.
load_file
(
filepath
,
system
.
resources_fs
,
location
)
definition_xml
=
cls
.
load_file
(
filepath
,
system
.
resources_fs
,
def_id
)
else
:
else
:
definition_xml
=
xml_object
definition_xml
=
xml_object
filepath
=
None
filepath
=
None
definition
,
children
=
cls
.
load_definition
(
definition_xml
,
system
,
location
)
# note this removes metadata
definition
,
children
=
cls
.
load_definition
(
definition_xml
,
system
,
def_id
)
# note this removes metadata
# VS[compat] -- make Ike's github preview links work in both old and
# VS[compat] -- make Ike's github preview links work in both old and
# new file layouts
# new file layouts
...
@@ -313,13 +311,11 @@ class XmlDescriptor(XModuleDescriptor):
...
@@ -313,13 +311,11 @@ class XmlDescriptor(XModuleDescriptor):
try
:
try
:
metadata
.
update
(
json
.
loads
(
dmdata
))
metadata
.
update
(
json
.
loads
(
dmdata
))
except
Exception
as
err
:
except
Exception
as
err
:
log
.
debug
(
'Error
%
s in loading metadata
%
s'
%
(
err
,
dmdata
)
)
log
.
debug
(
'Error
in loading metadata
%
r'
,
dmdata
,
exc_info
=
True
)
metadata
[
'definition_metadata_err'
]
=
str
(
err
)
metadata
[
'definition_metadata_err'
]
=
str
(
err
)
# Set/override any metadata specified by policy
# Set/override any metadata specified by policy
k
=
policy_key
(
location
)
cls
.
apply_policy
(
metadata
,
system
.
get_policy
(
usage_id
))
if
k
in
system
.
policy
:
cls
.
apply_policy
(
metadata
,
system
.
policy
[
k
])
field_data
=
{}
field_data
=
{}
field_data
.
update
(
metadata
)
field_data
.
update
(
metadata
)
...
@@ -327,17 +323,13 @@ class XmlDescriptor(XModuleDescriptor):
...
@@ -327,17 +323,13 @@ class XmlDescriptor(XModuleDescriptor):
field_data
[
'children'
]
=
children
field_data
[
'children'
]
=
children
field_data
[
'xml_attributes'
][
'filename'
]
=
definition
.
get
(
'filename'
,
[
''
,
None
])
# for git link
field_data
[
'xml_attributes'
][
'filename'
]
=
definition
.
get
(
'filename'
,
[
''
,
None
])
# for git link
field_data
[
'location'
]
=
location
field_data
[
'category'
]
=
xml_object
.
tag
kvs
=
InheritanceKeyValueStore
(
initial_values
=
field_data
)
kvs
=
InheritanceKeyValueStore
(
initial_values
=
field_data
)
field_data
=
DbModel
(
kvs
)
field_data
=
KvsFieldData
(
kvs
)
return
system
.
construct_xblock_from_class
(
return
system
.
construct_xblock_from_class
(
cls
,
cls
,
# We're loading a descriptor, so student_id is meaningless
# We're loading a descriptor, so student_id is meaningless
# We also don't have separate notions of definition and usage ids yet,
ScopeIds
(
None
,
xml_object
.
tag
,
def_id
,
usage_id
),
# so we use the location for both
ScopeIds
(
None
,
location
.
category
,
location
,
location
),
field_data
,
field_data
,
)
)
...
...
common/test/data/simple/course.xml
View file @
d2a0df41
...
@@ -27,5 +27,4 @@
...
@@ -27,5 +27,4 @@
</vertical>
</vertical>
</sequential>
</sequential>
</chapter>
</chapter>
</course>
</course>
lms/djangoapps/courseware/management/commands/dump_course_structure.py
View file @
d2a0df41
...
@@ -95,7 +95,7 @@ def dump_module(module, destination=None, inherited=False, defaults=False):
...
@@ -95,7 +95,7 @@ def dump_module(module, destination=None, inherited=False, defaults=False):
destination
[
module
.
location
.
url
()]
=
{
destination
[
module
.
location
.
url
()]
=
{
'category'
:
module
.
location
.
category
,
'category'
:
module
.
location
.
category
,
'children'
:
module
.
children
if
hasattr
(
module
,
'children'
)
else
[
],
'children'
:
[
str
(
child
)
for
child
in
getattr
(
module
,
'children'
,
[])
],
'metadata'
:
filtered_metadata
,
'metadata'
:
filtered_metadata
,
}
}
...
@@ -110,7 +110,7 @@ def dump_module(module, destination=None, inherited=False, defaults=False):
...
@@ -110,7 +110,7 @@ def dump_module(module, destination=None, inherited=False, defaults=False):
return
False
return
False
elif
field
.
scope
!=
Scope
.
settings
:
elif
field
.
scope
!=
Scope
.
settings
:
return
False
return
False
elif
defaults
==
True
:
elif
defaults
:
return
True
return
True
else
:
else
:
return
field
.
values
!=
field
.
default
return
field
.
values
!=
field
.
default
...
...
lms/djangoapps/courseware/model_data.py
View file @
d2a0df41
...
@@ -317,7 +317,7 @@ class DjangoKeyValueStore(KeyValueStore):
...
@@ -317,7 +317,7 @@ class DjangoKeyValueStore(KeyValueStore):
Provide a bulk save mechanism.
Provide a bulk save mechanism.
`kv_dict`: A dictionary of dirty fields that maps
`kv_dict`: A dictionary of dirty fields that maps
xblock.
DbModel
._key : value
xblock.
KvsFieldData
._key : value
"""
"""
saved_fields
=
[]
saved_fields
=
[]
...
...
lms/djangoapps/courseware/module_render.py
View file @
d2a0df41
...
@@ -29,7 +29,7 @@ from util.json_request import JsonResponse
...
@@ -29,7 +29,7 @@ from util.json_request import JsonResponse
from
util.sandboxing
import
can_execute_unsafe_code
from
util.sandboxing
import
can_execute_unsafe_code
from
xblock.core
import
XBlock
from
xblock.core
import
XBlock
from
xblock.fields
import
Scope
from
xblock.fields
import
Scope
from
xblock.runtime
import
DbModel
,
KeyValueStore
from
xblock.runtime
import
KvsFieldData
,
KeyValueStore
from
xblock.exceptions
import
NoSuchHandlerError
from
xblock.exceptions
import
NoSuchHandlerError
from
xblock.django.request
import
django_to_webob_request
,
webob_to_django_response
from
xblock.django.request
import
django_to_webob_request
,
webob_to_django_response
from
xmodule.error_module
import
ErrorDescriptor
,
NonStaffErrorDescriptor
from
xmodule.error_module
import
ErrorDescriptor
,
NonStaffErrorDescriptor
...
@@ -222,7 +222,7 @@ def get_module_for_descriptor_internal(user, descriptor, field_data_cache, cours
...
@@ -222,7 +222,7 @@ def get_module_for_descriptor_internal(user, descriptor, field_data_cache, cours
if
not
has_access
(
user
,
descriptor
,
'load'
,
course_id
):
if
not
has_access
(
user
,
descriptor
,
'load'
,
course_id
):
return
None
return
None
student_data
=
DbModel
(
DjangoKeyValueStore
(
field_data_cache
))
student_data
=
KvsFieldData
(
DjangoKeyValueStore
(
field_data_cache
))
descriptor
.
_field_data
=
LmsFieldData
(
descriptor
.
_field_data
,
student_data
)
descriptor
.
_field_data
=
LmsFieldData
(
descriptor
.
_field_data
,
student_data
)
...
...
requirements/edx/base.txt
View file @
d2a0df41
...
@@ -93,7 +93,6 @@ transifex-client==0.9.1
...
@@ -93,7 +93,6 @@ transifex-client==0.9.1
# Used for testing
# Used for testing
coverage==3.7
coverage==3.7
ddt==0.4.0
factory_boy==2.2.1
factory_boy==2.2.1
mock==1.0.1
mock==1.0.1
nosexcover==1.0.7
nosexcover==1.0.7
...
...
requirements/edx/github.txt
View file @
d2a0df41
...
@@ -15,10 +15,13 @@
...
@@ -15,10 +15,13 @@
-e git+https://github.com/eventbrite/zendesk.git@d53fe0e81b623f084e91776bcf6369f8b7b63879#egg=zendesk
-e git+https://github.com/eventbrite/zendesk.git@d53fe0e81b623f084e91776bcf6369f8b7b63879#egg=zendesk
# Our libraries:
# Our libraries:
-e git+https://github.com/edx/XBlock.git@
cd77808aadd3ea1c2027ca8c0aa5624d8ccccc5
2#egg=XBlock
-e git+https://github.com/edx/XBlock.git@
a1a3e76b269d15b7bbd11976d8aef63e1db6c4c
2#egg=XBlock
-e git+https://github.com/edx/codejail.git@e3d98f9455#egg=codejail
-e git+https://github.com/edx/codejail.git@e3d98f9455#egg=codejail
-e git+https://github.com/edx/diff-cover.git@v0.2.6#egg=diff_cover
-e git+https://github.com/edx/diff-cover.git@v0.2.6#egg=diff_cover
-e git+https://github.com/edx/js-test-tool.git@v0.1.5#egg=js_test_tool
-e git+https://github.com/edx/js-test-tool.git@v0.1.5#egg=js_test_tool
-e git+https://github.com/edx/django-waffle.git@823a102e48#egg=django-waffle
-e git+https://github.com/edx/django-waffle.git@823a102e48#egg=django-waffle
-e git+https://github.com/edx/event-tracking.git@f0211d702d#egg=event-tracking
-e git+https://github.com/edx/event-tracking.git@f0211d702d#egg=event-tracking
-e git+https://github.com/edx/bok-choy.git@bc6f1adbe439618162079f1004b2b3db3b6f8916#egg=bok_choy
-e git+https://github.com/edx/bok-choy.git@bc6f1adbe439618162079f1004b2b3db3b6f8916#egg=bok_choy
# Move back to upstream release once https://github.com/txels/ddt/pull/13 is merged
-e git+https://github.com/edx/ddt.git@9e8010b8777aa40b848fdb76de6e60081616325a#egg=ddt
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment