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
fc131fa8
Commit
fc131fa8
authored
Sep 05, 2013
by
Don Mitchell
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add InheritanceKVS and standardize inherited attr patterns
parent
8201b141
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
229 additions
and
267 deletions
+229
-267
cms/djangoapps/contentstore/tests/test_crud.py
+1
-1
cms/static/coffee/spec/views/metadata_edit_spec.coffee
+0
-7
common/lib/xmodule/xmodule/modulestore/inheritance.py
+49
-53
common/lib/xmodule/xmodule/modulestore/mongo/base.py
+5
-9
common/lib/xmodule/xmodule/modulestore/split_mongo/split_mongo_kvs.py
+8
-52
common/lib/xmodule/xmodule/tests/__init__.py
+17
-2
common/lib/xmodule/xmodule/tests/test_editing_module.py
+4
-4
common/lib/xmodule/xmodule/tests/test_import.py
+9
-13
common/lib/xmodule/xmodule/tests/test_video.py
+12
-7
common/lib/xmodule/xmodule/tests/test_xblock_wrappers.py
+9
-20
common/lib/xmodule/xmodule/tests/test_xml_module.py
+42
-32
common/lib/xmodule/xmodule/video_module.py
+13
-12
common/lib/xmodule/xmodule/x_module.py
+55
-51
common/lib/xmodule/xmodule/xml_module.py
+5
-4
No files found.
cms/djangoapps/contentstore/tests/test_crud.py
View file @
fc131fa8
...
@@ -218,7 +218,7 @@ class TemplateTests(unittest.TestCase):
...
@@ -218,7 +218,7 @@ class TemplateTests(unittest.TestCase):
)
)
usage_id
=
json_data
.
get
(
'_id'
,
None
)
usage_id
=
json_data
.
get
(
'_id'
,
None
)
if
not
'_inherited_settings'
in
json_data
and
parent_xblock
is
not
None
:
if
not
'_inherited_settings'
in
json_data
and
parent_xblock
is
not
None
:
json_data
[
'_inherited_settings'
]
=
parent_xblock
.
xblock_kvs
.
get_inherited_settings
()
.
copy
()
json_data
[
'_inherited_settings'
]
=
parent_xblock
.
xblock_kvs
.
inherited_settings
.
copy
()
json_fields
=
json_data
.
get
(
'fields'
,
{})
json_fields
=
json_data
.
get
(
'fields'
,
{})
for
field_name
in
inheritance
.
InheritanceMixin
.
fields
:
for
field_name
in
inheritance
.
InheritanceMixin
.
fields
:
if
field_name
in
json_fields
:
if
field_name
in
json_fields
:
...
...
cms/static/coffee/spec/views/metadata_edit_spec.coffee
View file @
fc131fa8
...
@@ -26,7 +26,6 @@ describe "Test Metadata Editor", ->
...
@@ -26,7 +26,6 @@ describe "Test Metadata Editor", ->
explicitly_set
:
true
,
explicitly_set
:
true
,
field_name
:
"display_name"
,
field_name
:
"display_name"
,
help
:
"Specifies the name for this component."
,
help
:
"Specifies the name for this component."
,
inheritable
:
false
,
options
:
[],
options
:
[],
type
:
CMS
.
Models
.
Metadata
.
GENERIC_TYPE
,
type
:
CMS
.
Models
.
Metadata
.
GENERIC_TYPE
,
value
:
"Word cloud"
value
:
"Word cloud"
...
@@ -38,7 +37,6 @@ describe "Test Metadata Editor", ->
...
@@ -38,7 +37,6 @@ describe "Test Metadata Editor", ->
explicitly_set
:
false
,
explicitly_set
:
false
,
field_name
:
"show_answer"
,
field_name
:
"show_answer"
,
help
:
"When should you show the answer"
,
help
:
"When should you show the answer"
,
inheritable
:
true
,
options
:
[
options
:
[
{
"display_name"
:
"Always"
,
"value"
:
"always"
},
{
"display_name"
:
"Always"
,
"value"
:
"always"
},
{
"display_name"
:
"Answered"
,
"value"
:
"answered"
},
{
"display_name"
:
"Answered"
,
"value"
:
"answered"
},
...
@@ -54,7 +52,6 @@ describe "Test Metadata Editor", ->
...
@@ -54,7 +52,6 @@ describe "Test Metadata Editor", ->
explicitly_set
:
false
,
explicitly_set
:
false
,
field_name
:
"num_inputs"
,
field_name
:
"num_inputs"
,
help
:
"Number of text boxes for student to input words/sentences."
,
help
:
"Number of text boxes for student to input words/sentences."
,
inheritable
:
false
,
options
:
{
min
:
1
},
options
:
{
min
:
1
},
type
:
CMS
.
Models
.
Metadata
.
INTEGER_TYPE
,
type
:
CMS
.
Models
.
Metadata
.
INTEGER_TYPE
,
value
:
5
value
:
5
...
@@ -66,7 +63,6 @@ describe "Test Metadata Editor", ->
...
@@ -66,7 +63,6 @@ describe "Test Metadata Editor", ->
explicitly_set
:
true
,
explicitly_set
:
true
,
field_name
:
"weight"
,
field_name
:
"weight"
,
help
:
"Weight for this problem"
,
help
:
"Weight for this problem"
,
inheritable
:
true
,
options
:
{
min
:
1.3
,
max
:
100.2
,
step
:
0.1
},
options
:
{
min
:
1.3
,
max
:
100.2
,
step
:
0.1
},
type
:
CMS
.
Models
.
Metadata
.
FLOAT_TYPE
,
type
:
CMS
.
Models
.
Metadata
.
FLOAT_TYPE
,
value
:
10.2
value
:
10.2
...
@@ -78,7 +74,6 @@ describe "Test Metadata Editor", ->
...
@@ -78,7 +74,6 @@ describe "Test Metadata Editor", ->
explicitly_set
:
false
,
explicitly_set
:
false
,
field_name
:
"list"
,
field_name
:
"list"
,
help
:
"A list of things."
,
help
:
"A list of things."
,
inheritable
:
false
,
options
:
[],
options
:
[],
type
:
CMS
.
Models
.
Metadata
.
LIST_TYPE
,
type
:
CMS
.
Models
.
Metadata
.
LIST_TYPE
,
value
:
[
"the first display value"
,
"the second"
]
value
:
[
"the first display value"
,
"the second"
]
...
@@ -99,7 +94,6 @@ describe "Test Metadata Editor", ->
...
@@ -99,7 +94,6 @@ describe "Test Metadata Editor", ->
explicitly_set
:
true
,
explicitly_set
:
true
,
field_name
:
"unknown_type"
,
field_name
:
"unknown_type"
,
help
:
"Mystery property."
,
help
:
"Mystery property."
,
inheritable
:
false
,
options
:
[
options
:
[
{
"display_name"
:
"Always"
,
"value"
:
"always"
},
{
"display_name"
:
"Always"
,
"value"
:
"always"
},
{
"display_name"
:
"Answered"
,
"value"
:
"answered"
},
{
"display_name"
:
"Answered"
,
"value"
:
"answered"
},
...
@@ -145,7 +139,6 @@ describe "Test Metadata Editor", ->
...
@@ -145,7 +139,6 @@ describe "Test Metadata Editor", ->
explicitly_set
:
false
,
explicitly_set
:
false
,
field_name
:
"display_name"
,
field_name
:
"display_name"
,
help
:
""
,
help
:
""
,
inheritable
:
false
,
options
:
[],
options
:
[],
type
:
CMS
.
Models
.
Metadata
.
GENERIC_TYPE
,
type
:
CMS
.
Models
.
Metadata
.
GENERIC_TYPE
,
value
:
null
value
:
null
...
...
common/lib/xmodule/xmodule/modulestore/inheritance.py
View file @
fc131fa8
...
@@ -3,6 +3,7 @@ from pytz import UTC
...
@@ -3,6 +3,7 @@ from pytz import UTC
from
xblock.fields
import
Scope
,
Boolean
,
String
,
Float
,
XBlockMixin
from
xblock.fields
import
Scope
,
Boolean
,
String
,
Float
,
XBlockMixin
from
xmodule.fields
import
Date
,
Timedelta
from
xmodule.fields
import
Date
,
Timedelta
from
xblock.runtime
import
KeyValueStore
class
InheritanceMixin
(
XBlockMixin
):
class
InheritanceMixin
(
XBlockMixin
):
...
@@ -51,16 +52,18 @@ def compute_inherited_metadata(descriptor):
...
@@ -51,16 +52,18 @@ def compute_inherited_metadata(descriptor):
NOTE: This means that there is no such thing as lazy loading at the
NOTE: This means that there is no such thing as lazy loading at the
moment--this accesses all the children."""
moment--this accesses all the children."""
for
child
in
descriptor
.
get_children
():
if
descriptor
.
has_children
:
inherit_metadata
(
parent_metadata
=
descriptor
.
xblock_kvs
.
inherited_settings
.
copy
()
child
,
# add any of descriptor's explicitly set fields to the inheriting list
{
for
field
in
InheritanceMixin
.
fields
.
values
():
name
:
field
.
read_from
(
descriptor
)
# pylint: disable = W0212
for
name
,
field
in
InheritanceMixin
.
fields
.
items
()
if
descriptor
.
_field_data
.
has
(
descriptor
,
field
.
name
):
if
field
.
is_set_on
(
descriptor
)
# inherited_settings values are json repr
}
parent_metadata
[
field
.
name
]
=
field
.
read_json
(
descriptor
)
)
compute_inherited_metadata
(
child
)
for
child
in
descriptor
.
get_children
():
inherit_metadata
(
child
,
parent_metadata
)
compute_inherited_metadata
(
child
)
def
inherit_metadata
(
descriptor
,
inherited_data
):
def
inherit_metadata
(
descriptor
,
inherited_data
):
...
@@ -72,53 +75,46 @@ def inherit_metadata(descriptor, inherited_data):
...
@@ -72,53 +75,46 @@ def inherit_metadata(descriptor, inherited_data):
`inherited_data`: A dictionary mapping field names to the values that
`inherited_data`: A dictionary mapping field names to the values that
they should inherit
they should inherit
"""
"""
# The inherited values that are actually being used.
try
:
if
not
hasattr
(
descriptor
,
'_inherited_metadata'
):
descriptor
.
xblock_kvs
.
inherited_settings
=
inherited_data
setattr
(
descriptor
,
'_inherited_metadata'
,
{})
except
AttributeError
:
# the kvs doesn't have inherited_settings probably b/c it's an error module
pass
# All inheritable metadata values (for which a value exists in field_data).
if
not
hasattr
(
descriptor
,
'_inheritable_metadata'
):
setattr
(
descriptor
,
'_inheritable_metadata'
,
{})
# Set all inheritable metadata from kwargs that are
# in self.inheritable_metadata and aren't already set in metadata
for
name
,
field
in
InheritanceMixin
.
fields
.
items
():
if
name
not
in
inherited_data
:
continue
inherited_value
=
inherited_data
[
name
]
descriptor
.
_inheritable_metadata
[
name
]
=
inherited_value
if
not
field
.
is_set_on
(
descriptor
):
descriptor
.
_inherited_metadata
[
name
]
=
inherited_value
field
.
write_to
(
descriptor
,
inherited_value
)
# We've updated the fields on the descriptor, so we need to save it
descriptor
.
save
()
def
own_metadata
(
module
):
def
own_metadata
(
module
):
# IN SPLIT MONGO this is just ['metadata'] as it keeps ['_inherited_metadata'] separate!
# FIXME move into kvs? will that work for xml mongo?
"""
"""
Return a dictionary that contains only non-inherited field keys,
Return a dictionary that contains only non-inherited field keys,
mapped to their serialized values
mapped to their serialized values
"""
"""
inherited_metadata
=
getattr
(
module
,
'_inherited_metadata'
,
{})
return
module
.
get_explicitly_set_fields_by_scope
(
Scope
.
settings
)
metadata
=
{}
for
name
,
field
in
module
.
fields
.
items
():
class
InheritanceKeyValueStore
(
KeyValueStore
):
# Only save metadata that wasn't inherited
"""
if
field
.
scope
!=
Scope
.
settings
:
Common superclass for kvs's which know about inheritance of settings. Offers simple
continue
dict-based storage of fields and lookup of inherited values.
if
not
field
.
is_set_on
(
module
):
Note: inherited_settings is a dict of key to json values (internal xblock field repr)
continue
"""
def
__init__
(
self
,
initial_values
=
None
,
inherited_settings
=
None
):
if
name
in
inherited_metadata
and
field
.
read_from
(
module
)
==
inherited_metadata
.
get
(
name
):
super
(
InheritanceKeyValueStore
,
self
)
.
__init__
()
continue
self
.
inherited_settings
=
inherited_settings
or
{}
self
.
_fields
=
initial_values
or
{}
try
:
metadata
[
name
]
=
field
.
read_json
(
module
)
def
get
(
self
,
key
):
except
KeyError
:
return
self
.
_fields
[
key
.
field_name
]
# Ignore any missing keys in _field_data
pass
def
set
(
self
,
key
,
value
):
# xml backed courses are read-only, but they do have some computed fields
return
metadata
self
.
_fields
[
key
.
field_name
]
=
value
def
delete
(
self
,
key
):
del
self
.
_fields
[
key
.
field_name
]
def
has
(
self
,
key
):
return
key
.
field_name
in
self
.
_fields
def
default
(
self
,
key
):
"""
Check to see if the default should be from inheritance rather than from the field's global default
"""
return
self
.
inherited_settings
[
key
.
field_name
]
common/lib/xmodule/xmodule/modulestore/mongo/base.py
View file @
fc131fa8
...
@@ -17,25 +17,23 @@ import sys
...
@@ -17,25 +17,23 @@ import sys
import
logging
import
logging
import
copy
import
copy
from
collections
import
namedtuple
from
fs.osfs
import
OSFS
from
fs.osfs
import
OSFS
from
itertools
import
repeat
from
itertools
import
repeat
from
path
import
path
from
path
import
path
from
operator
import
attrgetter
from
operator
import
attrgetter
from
uuid
import
uuid4
from
importlib
import
import_module
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.x_module
import
XModuleDescriptor
from
xmodule.x_module
import
XModuleDescriptor
from
xmodule.error_module
import
ErrorDescriptor
from
xmodule.error_module
import
ErrorDescriptor
from
xblock.runtime
import
DbModel
,
KeyValueStore
from
xblock.runtime
import
DbModel
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
ModuleStoreBase
,
Location
,
namedtuple_to_son
,
MONGO_MODULESTORE_TYPE
from
xmodule.modulestore
import
ModuleStoreBase
,
Location
,
namedtuple_to_son
,
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
from
xmodule.modulestore.inheritance
import
own_metadata
,
InheritanceMixin
,
inherit_metadata
,
InheritanceKeyValueStore
log
=
logging
.
getLogger
(
__name__
)
log
=
logging
.
getLogger
(
__name__
)
...
@@ -58,12 +56,13 @@ class InvalidWriteError(Exception):
...
@@ -58,12 +56,13 @@ class InvalidWriteError(Exception):
"""
"""
class
MongoKeyValueStore
(
KeyValueStore
):
class
MongoKeyValueStore
(
Inheritance
KeyValueStore
):
"""
"""
A KeyValueStore that maps keyed data access to one of the 3 data areas
A KeyValueStore that maps keyed data access to one of the 3 data areas
known to the MongoModuleStore (data, children, and metadata)
known to the MongoModuleStore (data, children, and metadata)
"""
"""
def
__init__
(
self
,
data
,
children
,
metadata
):
def
__init__
(
self
,
data
,
children
,
metadata
):
super
(
MongoKeyValueStore
,
self
)
.
__init__
()
self
.
_data
=
data
self
.
_data
=
data
self
.
_children
=
children
self
.
_children
=
children
self
.
_metadata
=
metadata
self
.
_metadata
=
metadata
...
@@ -201,10 +200,7 @@ class CachingDescriptorSystem(MakoDescriptorSystem):
...
@@ -201,10 +200,7 @@ class CachingDescriptorSystem(MakoDescriptorSystem):
# Convert the serialized fields values in self.cached_metadata
# Convert the serialized fields values in self.cached_metadata
# to python values
# to python values
metadata_to_inherit
=
{
metadata_to_inherit
=
self
.
cached_metadata
.
get
(
non_draft_loc
.
url
(),
{})
key
:
module
.
fields
[
key
]
.
from_json
(
value
)
for
key
,
value
in
self
.
cached_metadata
.
get
(
non_draft_loc
.
url
(),
{})
.
items
()
}
inherit_metadata
(
module
,
metadata_to_inherit
)
inherit_metadata
(
module
,
metadata_to_inherit
)
# decache any computed pending field settings
# decache any computed pending field settings
module
.
save
()
module
.
save
()
...
...
common/lib/xmodule/xmodule/modulestore/split_mongo/split_mongo_kvs.py
View file @
fc131fa8
...
@@ -4,37 +4,31 @@ from collections import namedtuple
...
@@ -4,37 +4,31 @@ from collections import namedtuple
from
xblock.runtime
import
KeyValueStore
from
xblock.runtime
import
KeyValueStore
from
xblock.exceptions
import
InvalidScopeError
from
xblock.exceptions
import
InvalidScopeError
from
.definition_lazy_loader
import
DefinitionLazyLoader
from
.definition_lazy_loader
import
DefinitionLazyLoader
from
xmodule.modulestore.inheritance
import
InheritanceKeyValueStore
# id is a BlockUsageLocator, def_id is the definition's guid
# id is a BlockUsageLocator, def_id is the definition's guid
SplitMongoKVSid
=
namedtuple
(
'SplitMongoKVSid'
,
'id, def_id'
)
SplitMongoKVSid
=
namedtuple
(
'SplitMongoKVSid'
,
'id, def_id'
)
PROVENANCE_LOCAL
=
'local'
class
SplitMongoKVS
(
InheritanceKeyValueStore
):
PROVENANCE_DEFAULT
=
'default'
PROVENANCE_INHERITED
=
'inherited'
class
SplitMongoKVS
(
KeyValueStore
):
"""
"""
A KeyValueStore that maps keyed data access to one of the 3 data areas
A KeyValueStore that maps keyed data access to one of the 3 data areas
known to the MongoModuleStore (data, children, and metadata)
known to the MongoModuleStore (data, children, and metadata)
"""
"""
def
__init__
(
self
,
definition
,
fields
,
_
inherited_settings
):
def
__init__
(
self
,
definition
,
fields
,
inherited_settings
):
"""
"""
:param definition: either a lazyloader or definition id for the definition
:param definition: either a lazyloader or definition id for the definition
:param fields: a dictionary of the locally set fields
:param fields: a dictionary of the locally set fields
:param
_inherited_settings: the
value of each inheritable field from above this.
:param
inherited_settings: the json
value of each inheritable field from above this.
Note, local fields may override and disagree w/ this b/c this says what the value
Note, local fields may override and disagree w/ this b/c this says what the value
should be if the field is undefined.
should be if the field is undefined.
"""
"""
# ensure kvs's don't share objects w/ others so that changes can't appear in separate ones
super
(
SplitMongoKVS
,
self
)
.
__init__
(
copy
.
copy
(
fields
),
inherited_settings
)
# the particular use case was that changes to kvs's were polluting caches. My thinking was
# that kvs's should be independent thus responsible for the isolation.
self
.
_definition
=
definition
# either a DefinitionLazyLoader or the db id of the definition.
self
.
_definition
=
definition
# either a DefinitionLazyLoader or the db id of the definition.
# if the db id, then the definition is presumed to be loaded into _fields
# if the db id, then the definition is presumed to be loaded into _fields
self
.
_fields
=
copy
.
copy
(
fields
)
self
.
_inherited_settings
=
_inherited_settings
def
get
(
self
,
key
):
def
get
(
self
,
key
):
# simplest case, field is directly set
# simplest case, field is directly set
...
@@ -49,12 +43,8 @@ class SplitMongoKVS(KeyValueStore):
...
@@ -49,12 +43,8 @@ class SplitMongoKVS(KeyValueStore):
# didn't find children in _fields; so, see if there's a default
# didn't find children in _fields; so, see if there's a default
raise
KeyError
()
raise
KeyError
()
elif
key
.
scope
==
Scope
.
settings
:
elif
key
.
scope
==
Scope
.
settings
:
# didn't find in _fields; so, get from inheritance since not locally set
# get default which may be the inherited value
if
key
.
field_name
in
self
.
_inherited_settings
:
raise
KeyError
()
return
self
.
_inherited_settings
[
key
.
field_name
]
else
:
# or get default
raise
KeyError
()
elif
key
.
scope
==
Scope
.
content
:
elif
key
.
scope
==
Scope
.
content
:
if
isinstance
(
self
.
_definition
,
DefinitionLazyLoader
):
if
isinstance
(
self
.
_definition
,
DefinitionLazyLoader
):
self
.
_load_definition
()
self
.
_load_definition
()
...
@@ -113,40 +103,6 @@ class SplitMongoKVS(KeyValueStore):
...
@@ -113,40 +103,6 @@ class SplitMongoKVS(KeyValueStore):
# if someone changes it so that they do, then change any tests of field.name in xx._field_data
# if someone changes it so that they do, then change any tests of field.name in xx._field_data
return
key
.
field_name
in
self
.
_fields
return
key
.
field_name
in
self
.
_fields
# would like to just take a key, but there's a bunch of magic in DbModel for constructing the key via
# a private method
def
field_value_provenance
(
self
,
key_scope
,
key_name
):
"""
Where the field value comes from: one of [PROVENANCE_LOCAL, PROVENANCE_DEFAULT, PROVENANCE_INHERITED].
"""
# handle any special cases
if
key_scope
==
Scope
.
content
:
if
key_name
==
'location'
:
return
PROVENANCE_LOCAL
elif
key_name
==
'category'
:
return
PROVENANCE_LOCAL
else
:
self
.
_load_definition
()
if
key_name
in
self
.
_fields
:
return
PROVENANCE_LOCAL
else
:
return
PROVENANCE_DEFAULT
elif
key_scope
==
Scope
.
parent
:
return
PROVENANCE_DEFAULT
# catch the locally set state
elif
key_name
in
self
.
_fields
:
return
PROVENANCE_LOCAL
elif
key_scope
==
Scope
.
settings
and
key_name
in
self
.
_inherited_settings
:
return
PROVENANCE_INHERITED
else
:
return
PROVENANCE_DEFAULT
def
get_inherited_settings
(
self
):
"""
Get the settings set by the ancestors (which locally set fields may override or not)
"""
return
self
.
_inherited_settings
def
_load_definition
(
self
):
def
_load_definition
(
self
):
"""
"""
Update fields w/ the lazily loaded definitions
Update fields w/ the lazily loaded definitions
...
...
common/lib/xmodule/xmodule/tests/__init__.py
View file @
fc131fa8
...
@@ -19,7 +19,9 @@ from path import path
...
@@ -19,7 +19,9 @@ from path import path
import
calc
import
calc
from
xblock.field_data
import
DictFieldData
from
xblock.field_data
import
DictFieldData
from
xmodule.x_module
import
ModuleSystem
,
XModuleDescriptor
from
xmodule.x_module
import
ModuleSystem
,
XModuleDescriptor
,
DescriptorSystem
from
xmodule.modulestore.inheritance
import
InheritanceMixin
from
xmodule.mako_module
import
MakoDescriptorSystem
# Location of common test DATA directory
# Location of common test DATA directory
...
@@ -64,7 +66,20 @@ def get_test_system():
...
@@ -64,7 +66,20 @@ def get_test_system():
node_path
=
os
.
environ
.
get
(
"NODE_PATH"
,
"/usr/local/lib/node_modules"
),
node_path
=
os
.
environ
.
get
(
"NODE_PATH"
,
"/usr/local/lib/node_modules"
),
xblock_field_data
=
lambda
descriptor
:
descriptor
.
_field_data
,
xblock_field_data
=
lambda
descriptor
:
descriptor
.
_field_data
,
anonymous_student_id
=
'student'
,
anonymous_student_id
=
'student'
,
open_ended_grading_interface
=
open_ended_grading_interface
open_ended_grading_interface
=
open_ended_grading_interface
)
def
get_test_descriptor_system
():
"""
Construct a test DescriptorSystem instance.
"""
return
MakoDescriptorSystem
(
load_item
=
Mock
(),
resources_fs
=
Mock
(),
error_tracker
=
Mock
(),
render_template
=
lambda
template
,
context
:
repr
(
context
),
mixins
=
(
InheritanceMixin
,),
)
)
...
...
common/lib/xmodule/xmodule/tests/test_editing_module.py
View file @
fc131fa8
...
@@ -9,7 +9,7 @@ from xmodule.editing_module import TabsEditingDescriptor
...
@@ -9,7 +9,7 @@ from xmodule.editing_module import TabsEditingDescriptor
from
xblock.field_data
import
DictFieldData
from
xblock.field_data
import
DictFieldData
from
xblock.fields
import
ScopeIds
from
xblock.fields
import
ScopeIds
from
.
import
get_test
_system
from
xmodule.tests
import
get_test_descriptor
_system
log
=
logging
.
getLogger
(
__name__
)
log
=
logging
.
getLogger
(
__name__
)
...
@@ -19,7 +19,7 @@ class TabsEditingDescriptorTestCase(unittest.TestCase):
...
@@ -19,7 +19,7 @@ class TabsEditingDescriptorTestCase(unittest.TestCase):
def
setUp
(
self
):
def
setUp
(
self
):
super
(
TabsEditingDescriptorTestCase
,
self
)
.
setUp
()
super
(
TabsEditingDescriptorTestCase
,
self
)
.
setUp
()
system
=
get_test_system
()
system
=
get_test_
descriptor_
system
()
system
.
render_template
=
Mock
(
return_value
=
"<div>Test Template HTML</div>"
)
system
.
render_template
=
Mock
(
return_value
=
"<div>Test Template HTML</div>"
)
self
.
tabs
=
[
self
.
tabs
=
[
{
{
...
@@ -44,8 +44,8 @@ class TabsEditingDescriptorTestCase(unittest.TestCase):
...
@@ -44,8 +44,8 @@ class TabsEditingDescriptorTestCase(unittest.TestCase):
]
]
TabsEditingDescriptor
.
tabs
=
self
.
tabs
TabsEditingDescriptor
.
tabs
=
self
.
tabs
self
.
descriptor
=
TabsEditingDescriptor
(
self
.
descriptor
=
system
.
construct_xblock_from_class
(
runtime
=
system
,
TabsEditingDescriptor
,
field_data
=
DictFieldData
({}),
field_data
=
DictFieldData
({}),
scope_ids
=
ScopeIds
(
None
,
None
,
None
,
None
),
scope_ids
=
ScopeIds
(
None
,
None
,
None
,
None
),
)
)
...
...
common/lib/xmodule/xmodule/tests/test_import.py
View file @
fc131fa8
...
@@ -158,11 +158,10 @@ class ImportTestCase(BaseCourseTestCase):
...
@@ -158,11 +158,10 @@ class ImportTestCase(BaseCourseTestCase):
# Check that the child inherits due correctly
# Check that the child inherits due correctly
child
=
descriptor
.
get_children
()[
0
]
child
=
descriptor
.
get_children
()[
0
]
self
.
assertEqual
(
child
.
due
,
ImportTestCase
.
date
.
from_json
(
v
))
self
.
assertEqual
(
child
.
due
,
ImportTestCase
.
date
.
from_json
(
v
))
self
.
assertEqual
(
child
.
_inheritable_metadata
,
child
.
_inherited_metadata
)
# need to convert v to canonical json b4 comparing
self
.
assertEqual
(
1
,
len
(
child
.
_inherited_metadata
))
self
.
assertEqual
(
self
.
assertEqual
(
datetime
.
datetime
(
2013
,
3
,
20
,
17
,
0
,
tzinfo
=
UTC
(
)),
ImportTestCase
.
date
.
to_json
(
ImportTestCase
.
date
.
from_json
(
v
)),
child
.
_inherited_metadata
[
'due'
]
child
.
xblock_kvs
.
inherited_settings
[
'due'
]
)
)
# Now export and check things
# Now export and check things
...
@@ -218,8 +217,6 @@ class ImportTestCase(BaseCourseTestCase):
...
@@ -218,8 +217,6 @@ class ImportTestCase(BaseCourseTestCase):
# Check that the child does not inherit a value for due
# Check that the child does not inherit a value for due
child
=
descriptor
.
get_children
()[
0
]
child
=
descriptor
.
get_children
()[
0
]
self
.
assertEqual
(
child
.
due
,
None
)
self
.
assertEqual
(
child
.
due
,
None
)
# pylint: disable=W0212
self
.
assertEqual
(
child
.
_inheritable_metadata
,
child
.
_inherited_metadata
)
self
.
assertLessEqual
(
self
.
assertLessEqual
(
child
.
start
,
child
.
start
,
datetime
.
datetime
.
now
(
UTC
())
datetime
.
datetime
.
now
(
UTC
())
...
@@ -249,10 +246,9 @@ class ImportTestCase(BaseCourseTestCase):
...
@@ -249,10 +246,9 @@ class ImportTestCase(BaseCourseTestCase):
self
.
assertEqual
(
descriptor
.
due
,
ImportTestCase
.
date
.
from_json
(
course_due
))
self
.
assertEqual
(
descriptor
.
due
,
ImportTestCase
.
date
.
from_json
(
course_due
))
self
.
assertEqual
(
child
.
due
,
ImportTestCase
.
date
.
from_json
(
child_due
))
self
.
assertEqual
(
child
.
due
,
ImportTestCase
.
date
.
from_json
(
child_due
))
# Test inherited metadata. Due does not appear here (because explicitly set on child).
# Test inherited metadata. Due does not appear here (because explicitly set on child).
self
.
assertEqual
(
1
,
len
(
child
.
_inheritable_metadata
))
self
.
assertEqual
(
self
.
assertEqual
(
datetime
.
datetime
(
2013
,
3
,
20
,
17
,
0
,
tzinfo
=
UTC
(
)),
ImportTestCase
.
date
.
to_json
(
ImportTestCase
.
date
.
from_json
(
course_due
)),
child
.
_inheritable_metadata
[
'due'
]
child
.
xblock_kvs
.
inherited_settings
[
'due'
]
)
)
def
test_is_pointer_tag
(
self
):
def
test_is_pointer_tag
(
self
):
...
@@ -288,14 +284,14 @@ class ImportTestCase(BaseCourseTestCase):
...
@@ -288,14 +284,14 @@ class ImportTestCase(BaseCourseTestCase):
print
(
"Starting import"
)
print
(
"Starting import"
)
course
=
self
.
get_course
(
'toy'
)
course
=
self
.
get_course
(
'toy'
)
def
check_for_key
(
key
,
node
):
def
check_for_key
(
key
,
node
,
value
):
"recursive check for presence of key"
"recursive check for presence of key"
print
(
"Checking {0}"
.
format
(
node
.
location
.
url
()))
print
(
"Checking {0}"
.
format
(
node
.
location
.
url
()))
self
.
assert
True
(
node
.
_field_data
.
has
(
node
,
key
)
)
self
.
assert
Equal
(
getattr
(
node
,
key
),
value
)
for
c
in
node
.
get_children
():
for
c
in
node
.
get_children
():
check_for_key
(
key
,
c
)
check_for_key
(
key
,
c
,
value
)
check_for_key
(
'graceperiod'
,
course
)
check_for_key
(
'graceperiod'
,
course
,
course
.
graceperiod
)
def
test_policy_loading
(
self
):
def
test_policy_loading
(
self
):
"""Make sure that when two courses share content with the same
"""Make sure that when two courses share content with the same
...
...
common/lib/xmodule/xmodule/tests/test_video.py
View file @
fc131fa8
...
@@ -18,7 +18,6 @@ from mock import Mock
...
@@ -18,7 +18,6 @@ from mock import Mock
from
.
import
LogicTest
from
.
import
LogicTest
from
lxml
import
etree
from
lxml
import
etree
from
.
import
get_test_system
from
xmodule.modulestore
import
Location
from
xmodule.modulestore
import
Location
from
xmodule.video_module
import
VideoDescriptor
,
_create_youtube_string
from
xmodule.video_module
import
VideoDescriptor
,
_create_youtube_string
from
.test_import
import
DummySystem
from
.test_import
import
DummySystem
...
@@ -26,6 +25,7 @@ from xblock.field_data import DictFieldData
...
@@ -26,6 +25,7 @@ from xblock.field_data import DictFieldData
from
xblock.fields
import
ScopeIds
from
xblock.fields
import
ScopeIds
from
textwrap
import
dedent
from
textwrap
import
dedent
from
xmodule.tests
import
get_test_descriptor_system
class
VideoModuleTest
(
LogicTest
):
class
VideoModuleTest
(
LogicTest
):
...
@@ -124,9 +124,9 @@ class VideoDescriptorTest(unittest.TestCase):
...
@@ -124,9 +124,9 @@ class VideoDescriptorTest(unittest.TestCase):
"""Test for VideoDescriptor"""
"""Test for VideoDescriptor"""
def
setUp
(
self
):
def
setUp
(
self
):
system
=
get_test_system
()
system
=
get_test_
descriptor_
system
()
self
.
descriptor
=
VideoDescriptor
(
self
.
descriptor
=
system
.
construct_xblock_from_class
(
runtime
=
system
,
VideoDescriptor
,
field_data
=
DictFieldData
({}),
field_data
=
DictFieldData
({}),
scope_ids
=
ScopeIds
(
None
,
None
,
None
,
None
),
scope_ids
=
ScopeIds
(
None
,
None
,
None
,
None
),
)
)
...
@@ -304,7 +304,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
...
@@ -304,7 +304,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
a few weeks).
a few weeks).
"""
"""
module_system
=
DummySystem
(
load_error_modules
=
True
)
module_system
=
DummySystem
(
load_error_modules
=
True
)
xml_data
=
'''
xml_data
=
'''
<video display_name=""display_name""
<video display_name=""display_name""
html5_sources="["source_1", "source_2"]"
html5_sources="["source_1", "source_2"]"
show_captions="false"
show_captions="false"
...
@@ -418,6 +418,11 @@ class VideoExportTestCase(unittest.TestCase):
...
@@ -418,6 +418,11 @@ class VideoExportTestCase(unittest.TestCase):
Make sure that VideoDescriptor can export itself to XML
Make sure that VideoDescriptor can export itself to XML
correctly.
correctly.
"""
"""
def
assertXmlEqual
(
self
,
expected
,
xml
):
for
attr
in
[
'tag'
,
'attrib'
,
'text'
,
'tail'
]:
self
.
assertEqual
(
getattr
(
expected
,
attr
),
getattr
(
xml
,
attr
))
for
left
,
right
in
zip
(
expected
,
xml
):
self
.
assertXmlEqual
(
left
,
right
)
def
test_export_to_xml
(
self
):
def
test_export_to_xml
(
self
):
"""Test that we write the correct XML on export."""
"""Test that we write the correct XML on export."""
...
@@ -436,7 +441,7 @@ class VideoExportTestCase(unittest.TestCase):
...
@@ -436,7 +441,7 @@ class VideoExportTestCase(unittest.TestCase):
desc
.
html5_sources
=
[
'http://www.example.com/source.mp4'
,
'http://www.example.com/source.ogg'
]
desc
.
html5_sources
=
[
'http://www.example.com/source.mp4'
,
'http://www.example.com/source.ogg'
]
xml
=
desc
.
definition_to_xml
(
None
)
# We don't use the `resource_fs` parameter
xml
=
desc
.
definition_to_xml
(
None
)
# We don't use the `resource_fs` parameter
expected
=
dedent
(
'''
\
expected
=
etree
.
fromstring
(
'''
\
<video url_name="SampleProblem1" start_time="0:00:01" youtube="0.75:izygArpw-Qo,1.00:p2Q6BrNhdh8,1.25:1EeWXzPdhSA,1.50:rABDYkeK0x8" show_captions="false" end_time="0:01:00">
<video url_name="SampleProblem1" start_time="0:00:01" youtube="0.75:izygArpw-Qo,1.00:p2Q6BrNhdh8,1.25:1EeWXzPdhSA,1.50:rABDYkeK0x8" show_captions="false" end_time="0:01:00">
<source src="http://www.example.com/source.mp4"/>
<source src="http://www.example.com/source.mp4"/>
<source src="http://www.example.com/source.ogg"/>
<source src="http://www.example.com/source.ogg"/>
...
@@ -444,7 +449,7 @@ class VideoExportTestCase(unittest.TestCase):
...
@@ -444,7 +449,7 @@ class VideoExportTestCase(unittest.TestCase):
</video>
</video>
'''
)
'''
)
self
.
assert
Equals
(
expected
,
etree
.
tostring
(
xml
,
pretty_print
=
True
)
)
self
.
assert
XmlEqual
(
expected
,
xml
)
def
test_export_to_xml_empty_parameters
(
self
):
def
test_export_to_xml_empty_parameters
(
self
):
"""Test XML export with defaults."""
"""Test XML export with defaults."""
...
...
common/lib/xmodule/xmodule/tests/test_xblock_wrappers.py
View file @
fc131fa8
...
@@ -29,6 +29,7 @@ from xmodule.conditional_module import ConditionalDescriptor
...
@@ -29,6 +29,7 @@ from xmodule.conditional_module import ConditionalDescriptor
from
xmodule.randomize_module
import
RandomizeDescriptor
from
xmodule.randomize_module
import
RandomizeDescriptor
from
xmodule.vertical_module
import
VerticalDescriptor
from
xmodule.vertical_module
import
VerticalDescriptor
from
xmodule.wrapper_module
import
WrapperDescriptor
from
xmodule.wrapper_module
import
WrapperDescriptor
from
xmodule.tests
import
get_test_descriptor_system
LEAF_XMODULES
=
(
LEAF_XMODULES
=
(
AnnotatableDescriptor
,
AnnotatableDescriptor
,
...
@@ -80,20 +81,12 @@ class TestXBlockWrapper(object):
...
@@ -80,20 +81,12 @@ class TestXBlockWrapper(object):
)
)
return
runtime
return
runtime
@property
def
leaf_descriptor_runtime
(
self
):
runtime
=
MakoDescriptorSystem
(
load_item
=
Mock
(),
resources_fs
=
Mock
(),
error_tracker
=
Mock
(),
render_template
=
(
lambda
*
args
,
**
kwargs
:
u'{!r}, {!r}'
.
format
(
args
,
kwargs
)),
)
return
runtime
def
leaf_descriptor
(
self
,
descriptor_cls
):
def
leaf_descriptor
(
self
,
descriptor_cls
):
location
=
'i4x://org/course/category/name'
location
=
'i4x://org/course/category/name'
return
descriptor_cls
(
runtime
=
get_test_descriptor_system
()
self
.
leaf_descriptor_runtime
,
runtime
.
render_template
=
lambda
*
args
,
**
kwargs
:
u'{!r}, {!r}'
.
format
(
args
,
kwargs
)
return
runtime
.
construct_xblock_from_class
(
descriptor_cls
,
DictFieldData
({}),
DictFieldData
({}),
ScopeIds
(
None
,
descriptor_cls
.
__name__
,
location
,
location
)
ScopeIds
(
None
,
descriptor_cls
.
__name__
,
location
,
location
)
)
)
...
@@ -110,16 +103,12 @@ class TestXBlockWrapper(object):
...
@@ -110,16 +103,12 @@ class TestXBlockWrapper(object):
runtime
.
position
=
2
runtime
.
position
=
2
return
runtime
return
runtime
@property
def
container_descriptor_runtime
(
self
):
runtime
=
Mock
()
runtime
.
render_template
=
lambda
*
args
,
**
kwargs
:
u'{!r}, {!r}'
.
format
(
args
,
kwargs
)
return
runtime
def
container_descriptor
(
self
,
descriptor_cls
):
def
container_descriptor
(
self
,
descriptor_cls
):
location
=
'i4x://org/course/category/name'
location
=
'i4x://org/course/category/name'
return
descriptor_cls
(
runtime
=
get_test_descriptor_system
()
self
.
container_descriptor_runtime
,
runtime
.
render_template
=
lambda
*
args
,
**
kwargs
:
u'{!r}, {!r}'
.
format
(
args
,
kwargs
)
return
runtime
.
construct_xblock_from_class
(
descriptor_cls
,
DictFieldData
({
DictFieldData
({
'children'
:
range
(
3
)
'children'
:
range
(
3
)
}),
}),
...
...
common/lib/xmodule/xmodule/tests/test_xml_module.py
View file @
fc131fa8
...
@@ -7,9 +7,11 @@ from xblock.field_data import DictFieldData
...
@@ -7,9 +7,11 @@ from xblock.field_data import DictFieldData
from
xmodule.fields
import
Date
,
Timedelta
from
xmodule.fields
import
Date
,
Timedelta
from
xmodule.xml_module
import
XmlDescriptor
,
serialize_field
,
deserialize_field
from
xmodule.xml_module
import
XmlDescriptor
,
serialize_field
,
deserialize_field
import
unittest
import
unittest
from
.
import
get_test_system
from
nose.tools
import
assert_equals
# pylint: disable=E0611
from
nose.tools
import
assert_equals
# pylint: disable=E0611
from
mock
import
Mock
from
mock
import
Mock
from
xmodule.modulestore.inheritance
import
InheritanceKeyValueStore
,
InheritanceMixin
from
xblock.runtime
import
DbModel
from
xmodule.tests
import
get_test_descriptor_system
class
CrazyJsonString
(
String
):
class
CrazyJsonString
(
String
):
...
@@ -34,6 +36,11 @@ class TestFields(object):
...
@@ -34,6 +36,11 @@ class TestFields(object):
values
=
[{
'display_name'
:
'first'
,
'value'
:
'value a'
},
values
=
[{
'display_name'
:
'first'
,
'value'
:
'value a'
},
{
'display_name'
:
'second'
,
'value'
:
'value b'
}]
{
'display_name'
:
'second'
,
'value'
:
'value b'
}]
)
)
showanswer
=
String
(
help
=
"When to show the problem answer to the student"
,
scope
=
Scope
.
settings
,
default
=
"finished"
)
# Used for testing select type
# Used for testing select type
float_select
=
Float
(
scope
=
Scope
.
settings
,
default
=.
999
,
values
=
[
1.23
,
0.98
])
float_select
=
Float
(
scope
=
Scope
.
settings
,
default
=.
999
,
values
=
[
1.23
,
0.98
])
# Used for testing float type
# Used for testing float type
...
@@ -48,10 +55,10 @@ class EditableMetadataFieldsTest(unittest.TestCase):
...
@@ -48,10 +55,10 @@ class EditableMetadataFieldsTest(unittest.TestCase):
editable_fields
=
self
.
get_xml_editable_fields
(
DictFieldData
({}))
editable_fields
=
self
.
get_xml_editable_fields
(
DictFieldData
({}))
# Tests that the xblock fields (currently tags and name) get filtered out.
# Tests that the xblock fields (currently tags and name) get filtered out.
# Also tests that xml_attributes is filtered out of XmlDescriptor.
# Also tests that xml_attributes is filtered out of XmlDescriptor.
self
.
assertEqual
(
1
,
len
(
editable_fields
),
"Expected only 1 editable field for xml descriptor."
)
self
.
assertEqual
(
1
,
len
(
editable_fields
),
editable_fields
)
self
.
assert_field_values
(
self
.
assert_field_values
(
editable_fields
,
'display_name'
,
XModuleFields
.
display_name
,
editable_fields
,
'display_name'
,
XModuleFields
.
display_name
,
explicitly_set
=
False
,
inheritable
=
False
,
value
=
None
,
default_value
=
None
explicitly_set
=
False
,
value
=
None
,
default_value
=
None
)
)
def
test_override_default
(
self
):
def
test_override_default
(
self
):
...
@@ -59,50 +66,51 @@ class EditableMetadataFieldsTest(unittest.TestCase):
...
@@ -59,50 +66,51 @@ class EditableMetadataFieldsTest(unittest.TestCase):
editable_fields
=
self
.
get_xml_editable_fields
(
DictFieldData
({
'display_name'
:
'foo'
}))
editable_fields
=
self
.
get_xml_editable_fields
(
DictFieldData
({
'display_name'
:
'foo'
}))
self
.
assert_field_values
(
self
.
assert_field_values
(
editable_fields
,
'display_name'
,
XModuleFields
.
display_name
,
editable_fields
,
'display_name'
,
XModuleFields
.
display_name
,
explicitly_set
=
True
,
inheritable
=
False
,
value
=
'foo'
,
default_value
=
None
explicitly_set
=
True
,
value
=
'foo'
,
default_value
=
None
)
)
def
test_integer_field
(
self
):
def
test_integer_field
(
self
):
descriptor
=
self
.
get_descriptor
(
DictFieldData
({
'max_attempts'
:
'7'
}))
descriptor
=
self
.
get_descriptor
(
DictFieldData
({
'max_attempts'
:
'7'
}))
editable_fields
=
descriptor
.
editable_metadata_fields
editable_fields
=
descriptor
.
editable_metadata_fields
self
.
assertEqual
(
7
,
len
(
editable_fields
))
self
.
assertEqual
(
8
,
len
(
editable_fields
))
self
.
assert_field_values
(
self
.
assert_field_values
(
editable_fields
,
'max_attempts'
,
TestFields
.
max_attempts
,
editable_fields
,
'max_attempts'
,
TestFields
.
max_attempts
,
explicitly_set
=
True
,
inheritable
=
False
,
value
=
7
,
default_value
=
1000
,
type
=
'Integer'
,
explicitly_set
=
True
,
value
=
7
,
default_value
=
1000
,
type
=
'Integer'
,
options
=
TestFields
.
max_attempts
.
values
options
=
TestFields
.
max_attempts
.
values
)
)
self
.
assert_field_values
(
self
.
assert_field_values
(
editable_fields
,
'display_name'
,
TestFields
.
display_name
,
editable_fields
,
'display_name'
,
TestFields
.
display_name
,
explicitly_set
=
False
,
inheritable
=
False
,
value
=
'local default'
,
default_value
=
'local default'
explicitly_set
=
False
,
value
=
'local default'
,
default_value
=
'local default'
)
)
editable_fields
=
self
.
get_descriptor
(
DictFieldData
({}))
.
editable_metadata_fields
editable_fields
=
self
.
get_descriptor
(
DictFieldData
({}))
.
editable_metadata_fields
self
.
assert_field_values
(
self
.
assert_field_values
(
editable_fields
,
'max_attempts'
,
TestFields
.
max_attempts
,
editable_fields
,
'max_attempts'
,
TestFields
.
max_attempts
,
explicitly_set
=
False
,
inheritable
=
False
,
value
=
1000
,
default_value
=
1000
,
type
=
'Integer'
,
explicitly_set
=
False
,
value
=
1000
,
default_value
=
1000
,
type
=
'Integer'
,
options
=
TestFields
.
max_attempts
.
values
options
=
TestFields
.
max_attempts
.
values
)
)
def
test_inherited_field
(
self
):
def
test_inherited_field
(
self
):
model_val
=
{
'display_name'
:
'inherited'
}
kvs
=
InheritanceKeyValueStore
(
initial_values
=
{},
inherited_settings
=
{
'showanswer'
:
'inherited'
})
descriptor
=
self
.
get_descriptor
(
DictFieldData
(
model_val
))
model_data
=
DbModel
(
kvs
)
# Mimic an inherited value for display_name (inherited and inheritable are the same in this case).
descriptor
=
self
.
get_descriptor
(
model_data
)
descriptor
.
_inherited_metadata
=
model_val
descriptor
.
_inheritable_metadata
=
model_val
editable_fields
=
descriptor
.
editable_metadata_fields
editable_fields
=
descriptor
.
editable_metadata_fields
self
.
assert_field_values
(
self
.
assert_field_values
(
editable_fields
,
'
display_name'
,
TestFields
.
display_name
,
editable_fields
,
'
showanswer'
,
InheritanceMixin
.
showanswer
,
explicitly_set
=
False
,
inheritable
=
True
,
value
=
'inherited'
,
default_value
=
'inherited'
explicitly_set
=
False
,
value
=
'inherited'
,
default_value
=
'inherited'
)
)
descriptor
=
self
.
get_descriptor
(
DictFieldData
({
'display_name'
:
'explicit'
}))
# Mimic the case where display_name WOULD have been inherited, except we explicitly set it.
# Mimic the case where display_name WOULD have been inherited, except we explicitly set it.
descriptor
.
_inheritable_metadata
=
{
'display_name'
:
'inheritable value'
}
kvs
=
InheritanceKeyValueStore
(
descriptor
.
_inherited_metadata
=
{}
initial_values
=
{
'showanswer'
:
'explicit'
},
inherited_settings
=
{
'showanswer'
:
'inheritable value'
}
)
model_data
=
DbModel
(
kvs
)
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
(
editable_fields
,
'
display_name'
,
TestFields
.
display_name
,
editable_fields
,
'
showanswer'
,
InheritanceMixin
.
showanswer
,
explicitly_set
=
True
,
inheritable
=
True
,
value
=
'explicit'
,
default_value
=
'inheritable value'
explicitly_set
=
True
,
value
=
'explicit'
,
default_value
=
'inheritable value'
)
)
def
test_type_and_options
(
self
):
def
test_type_and_options
(
self
):
...
@@ -115,41 +123,44 @@ class EditableMetadataFieldsTest(unittest.TestCase):
...
@@ -115,41 +123,44 @@ class EditableMetadataFieldsTest(unittest.TestCase):
# Tests for select
# Tests for select
self
.
assert_field_values
(
self
.
assert_field_values
(
editable_fields
,
'string_select'
,
TestFields
.
string_select
,
editable_fields
,
'string_select'
,
TestFields
.
string_select
,
explicitly_set
=
False
,
inheritable
=
False
,
value
=
'default value'
,
default_value
=
'default value'
,
explicitly_set
=
False
,
value
=
'default value'
,
default_value
=
'default value'
,
type
=
'Select'
,
options
=
[{
'display_name'
:
'first'
,
'value'
:
'value a JSON'
},
type
=
'Select'
,
options
=
[{
'display_name'
:
'first'
,
'value'
:
'value a JSON'
},
{
'display_name'
:
'second'
,
'value'
:
'value b JSON'
}]
{
'display_name'
:
'second'
,
'value'
:
'value b JSON'
}]
)
)
self
.
assert_field_values
(
self
.
assert_field_values
(
editable_fields
,
'float_select'
,
TestFields
.
float_select
,
editable_fields
,
'float_select'
,
TestFields
.
float_select
,
explicitly_set
=
False
,
inheritable
=
False
,
value
=.
999
,
default_value
=.
999
,
explicitly_set
=
False
,
value
=.
999
,
default_value
=.
999
,
type
=
'Select'
,
options
=
[
1.23
,
0.98
]
type
=
'Select'
,
options
=
[
1.23
,
0.98
]
)
)
self
.
assert_field_values
(
self
.
assert_field_values
(
editable_fields
,
'boolean_select'
,
TestFields
.
boolean_select
,
editable_fields
,
'boolean_select'
,
TestFields
.
boolean_select
,
explicitly_set
=
False
,
inheritable
=
False
,
value
=
None
,
default_value
=
None
,
explicitly_set
=
False
,
value
=
None
,
default_value
=
None
,
type
=
'Select'
,
options
=
[{
'display_name'
:
"True"
,
"value"
:
True
},
{
'display_name'
:
"False"
,
"value"
:
False
}]
type
=
'Select'
,
options
=
[{
'display_name'
:
"True"
,
"value"
:
True
},
{
'display_name'
:
"False"
,
"value"
:
False
}]
)
)
# Test for float
# Test for float
self
.
assert_field_values
(
self
.
assert_field_values
(
editable_fields
,
'float_non_select'
,
TestFields
.
float_non_select
,
editable_fields
,
'float_non_select'
,
TestFields
.
float_non_select
,
explicitly_set
=
False
,
inheritable
=
False
,
value
=.
999
,
default_value
=.
999
,
explicitly_set
=
False
,
value
=.
999
,
default_value
=.
999
,
type
=
'Float'
,
options
=
{
'min'
:
0
,
'step'
:
.
3
}
type
=
'Float'
,
options
=
{
'min'
:
0
,
'step'
:
.
3
}
)
)
self
.
assert_field_values
(
self
.
assert_field_values
(
editable_fields
,
'list_field'
,
TestFields
.
list_field
,
editable_fields
,
'list_field'
,
TestFields
.
list_field
,
explicitly_set
=
False
,
inheritable
=
False
,
value
=
[],
default_value
=
[],
explicitly_set
=
False
,
value
=
[],
default_value
=
[],
type
=
'List'
type
=
'List'
)
)
# Start of helper methods
# Start of helper methods
def
get_xml_editable_fields
(
self
,
field_data
):
def
get_xml_editable_fields
(
self
,
field_data
):
system
=
get_test_system
()
runtime
=
get_test_descriptor_system
()
system
.
render_template
=
Mock
(
return_value
=
"<div>Test Template HTML</div>"
)
return
runtime
.
construct_xblock_from_class
(
return
XmlDescriptor
(
runtime
=
system
,
field_data
=
field_data
,
scope_ids
=
Mock
())
.
editable_metadata_fields
XmlDescriptor
,
field_data
=
field_data
,
scope_ids
=
Mock
()
)
.
editable_metadata_fields
def
get_descriptor
(
self
,
field_data
):
def
get_descriptor
(
self
,
field_data
):
class
TestModuleDescriptor
(
TestFields
,
XmlDescriptor
):
class
TestModuleDescriptor
(
TestFields
,
XmlDescriptor
):
...
@@ -159,11 +170,11 @@ class EditableMetadataFieldsTest(unittest.TestCase):
...
@@ -159,11 +170,11 @@ class EditableMetadataFieldsTest(unittest.TestCase):
non_editable_fields
.
append
(
TestModuleDescriptor
.
due
)
non_editable_fields
.
append
(
TestModuleDescriptor
.
due
)
return
non_editable_fields
return
non_editable_fields
system
=
get_test_system
()
system
=
get_test_
descriptor_
system
()
system
.
render_template
=
Mock
(
return_value
=
"<div>Test Template HTML</div>"
)
system
.
render_template
=
Mock
(
return_value
=
"<div>Test Template HTML</div>"
)
return
TestModuleDescriptor
(
runtime
=
system
,
field_data
=
field_data
,
scope_ids
=
Mock
())
return
system
.
construct_xblock_from_class
(
TestModuleDescriptor
,
field_data
=
field_data
,
scope_ids
=
Mock
())
def
assert_field_values
(
self
,
editable_fields
,
name
,
field
,
explicitly_set
,
inheritable
,
value
,
default_value
,
def
assert_field_values
(
self
,
editable_fields
,
name
,
field
,
explicitly_set
,
value
,
default_value
,
type
=
'Generic'
,
options
=
[]):
type
=
'Generic'
,
options
=
[]):
test_field
=
editable_fields
[
name
]
test_field
=
editable_fields
[
name
]
...
@@ -178,7 +189,6 @@ class EditableMetadataFieldsTest(unittest.TestCase):
...
@@ -178,7 +189,6 @@ class EditableMetadataFieldsTest(unittest.TestCase):
self
.
assertEqual
(
type
,
test_field
[
'type'
])
self
.
assertEqual
(
type
,
test_field
[
'type'
])
self
.
assertEqual
(
explicitly_set
,
test_field
[
'explicitly_set'
])
self
.
assertEqual
(
explicitly_set
,
test_field
[
'explicitly_set'
])
self
.
assertEqual
(
inheritable
,
test_field
[
'inheritable'
])
class
TestSerialize
(
unittest
.
TestCase
):
class
TestSerialize
(
unittest
.
TestCase
):
...
...
common/lib/xmodule/xmodule/video_module.py
View file @
fc131fa8
...
@@ -15,6 +15,8 @@ import logging
...
@@ -15,6 +15,8 @@ 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
time
from
django.http
import
Http404
from
django.http
import
Http404
from
django.conf
import
settings
from
django.conf
import
settings
...
@@ -28,8 +30,8 @@ from xblock.fields import Scope, String, Boolean, Float, List, Integer, ScopeIds
...
@@ -28,8 +30,8 @@ from xblock.fields import Scope, String, Boolean, Float, List, Integer, ScopeIds
from
xblock.field_data
import
DictFieldData
from
xblock.field_data
import
DictFieldData
import
datetim
e
from
xmodule.modulestore.inheritance
import
InheritanceKeyValueStor
e
import
time
from
xblock.runtime
import
DbModel
log
=
logging
.
getLogger
(
__name__
)
log
=
logging
.
getLogger
(
__name__
)
...
@@ -240,9 +242,11 @@ class VideoDescriptor(VideoFields, TabsEditingDescriptor, EmptyDataRawDescriptor
...
@@ -240,9 +242,11 @@ class VideoDescriptor(VideoFields, TabsEditingDescriptor, EmptyDataRawDescriptor
xml_data
=
etree
.
tostring
(
cls
.
load_file
(
filepath
,
system
.
resources_fs
,
location
))
xml_data
=
etree
.
tostring
(
cls
.
load_file
(
filepath
,
system
.
resources_fs
,
location
))
field_data
=
VideoDescriptor
.
_parse_video_xml
(
xml_data
)
field_data
=
VideoDescriptor
.
_parse_video_xml
(
xml_data
)
field_data
[
'location'
]
=
location
field_data
[
'location'
]
=
location
kvs
=
InheritanceKeyValueStore
(
initial_values
=
field_data
)
field_data
=
DbModel
(
kvs
)
video
=
system
.
construct_xblock_from_class
(
video
=
system
.
construct_xblock_from_class
(
cls
,
cls
,
DictFieldData
(
field_data
)
,
field_data
,
# 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,
...
@@ -259,25 +263,22 @@ class VideoDescriptor(VideoFields, TabsEditingDescriptor, EmptyDataRawDescriptor
...
@@ -259,25 +263,22 @@ class VideoDescriptor(VideoFields, TabsEditingDescriptor, EmptyDataRawDescriptor
youtube_string
=
_create_youtube_string
(
self
)
youtube_string
=
_create_youtube_string
(
self
)
# Mild workaround to ensure that tests pass -- if a field
# Mild workaround to ensure that tests pass -- if a field
# is set to its default value, we don't need to write it out.
# is set to its default value, we don't need to write it out.
if
youtube_string
==
'1.00:OEoXaMPEzfM'
:
if
youtube_string
and
youtube_string
!=
'1.00:OEoXaMPEzfM'
:
youtube_string
=
''
xml
.
set
(
'youtube'
,
unicode
(
youtube_string
))
xml
.
set
(
'url_name'
,
self
.
url_name
)
attrs
=
{
attrs
=
{
'display_name'
:
self
.
display_name
,
'display_name'
:
self
.
display_name
,
'show_captions'
:
json
.
dumps
(
self
.
show_captions
),
'show_captions'
:
json
.
dumps
(
self
.
show_captions
),
'youtube'
:
youtube_string
,
'start_time'
:
datetime
.
timedelta
(
seconds
=
self
.
start_time
),
'start_time'
:
datetime
.
timedelta
(
seconds
=
self
.
start_time
),
'end_time'
:
datetime
.
timedelta
(
seconds
=
self
.
end_time
),
'end_time'
:
datetime
.
timedelta
(
seconds
=
self
.
end_time
),
'sub'
:
self
.
sub
,
'sub'
:
self
.
sub
,
'url_name'
:
self
.
url_name
}
}
fields
=
{
field
.
name
:
field
for
field
in
self
.
fields
.
values
()}
for
key
,
value
in
attrs
.
items
():
for
key
,
value
in
attrs
.
items
():
# Mild workaround to ensure that tests pass -- if a field
# Mild workaround to ensure that tests pass -- if a field
# is set to its default value, we don't need to write it out.
# is set to its default value, we don't write it out.
if
key
in
fields
and
fields
[
key
]
.
default
==
getattr
(
self
,
key
):
continue
if
value
:
if
value
:
xml
.
set
(
key
,
unicode
(
value
))
if
key
in
self
.
fields
and
self
.
fields
[
key
]
.
is_set_on
(
self
):
xml
.
set
(
key
,
unicode
(
value
))
for
source
in
self
.
html5_sources
:
for
source
in
self
.
html5_sources
:
ele
=
etree
.
Element
(
'source'
)
ele
=
etree
.
Element
(
'source'
)
...
...
common/lib/xmodule/xmodule/x_module.py
View file @
fc131fa8
...
@@ -648,26 +648,17 @@ class XModuleDescriptor(XModuleFields, HTMLSnippet, ResourceTemplates, XBlock):
...
@@ -648,26 +648,17 @@ class XModuleDescriptor(XModuleFields, HTMLSnippet, ResourceTemplates, XBlock):
# We are not allowing editing of xblock tag and name fields at this time (for any component).
# We are not allowing editing of xblock tag and name fields at this time (for any component).
return
[
XBlock
.
tags
,
XBlock
.
name
]
return
[
XBlock
.
tags
,
XBlock
.
name
]
def
get_explicitly_set_fields_by_scope
(
self
,
scope
=
Scope
.
content
):
def
get_explicitly_set_fields_by_scope
(
self
,
scope
=
Scope
.
content
):
"""
"""
Get a dictionary of the fields for the given scope which are set explicitly on this xblock. (Including
Get a dictionary of the fields for the given scope which are set explicitly on this xblock. (Including
any set to None.)
any set to None.)
"""
"""
if
scope
==
Scope
.
settings
and
hasattr
(
self
,
'_inherited_metadata'
):
result
=
{}
inherited_metadata
=
getattr
(
self
,
'_inherited_metadata'
)
for
field
in
self
.
fields
.
values
():
result
=
{}
if
(
field
.
scope
==
scope
and
self
.
_field_data
.
has
(
self
,
field
.
name
)):
for
field
in
self
.
fields
.
values
():
result
[
field
.
name
]
=
self
.
_field_data
.
get
(
self
,
field
.
name
)
if
(
field
.
scope
==
scope
and
return
result
self
.
_field_data
.
has
(
self
,
field
.
name
)
and
field
.
name
not
in
inherited_metadata
):
result
[
field
.
name
]
=
self
.
_field_data
.
get
(
self
,
field
.
name
)
return
result
else
:
result
=
{}
for
field
in
self
.
fields
.
values
():
if
(
field
.
scope
==
scope
and
self
.
_field_data
.
has
(
self
,
field
.
name
)):
result
[
field
.
name
]
=
self
.
_field_data
.
get
(
self
,
field
.
name
)
return
result
@property
@property
def
editable_metadata_fields
(
self
):
def
editable_metadata_fields
(
self
):
...
@@ -676,8 +667,14 @@ class XModuleDescriptor(XModuleFields, HTMLSnippet, ResourceTemplates, XBlock):
...
@@ -676,8 +667,14 @@ class XModuleDescriptor(XModuleFields, HTMLSnippet, ResourceTemplates, XBlock):
Can be limited by extending `non_editable_metadata_fields`.
Can be limited by extending `non_editable_metadata_fields`.
"""
"""
inherited_metadata
=
getattr
(
self
,
'_inherited_metadata'
,
{})
def
jsonify_value
(
field
,
json_choice
):
inheritable_metadata
=
getattr
(
self
,
'_inheritable_metadata'
,
{})
if
isinstance
(
json_choice
,
dict
)
and
'value'
in
json_choice
:
json_choice
=
dict
(
json_choice
)
# make a copy so below doesn't change the original
json_choice
[
'value'
]
=
field
.
to_json
(
json_choice
[
'value'
])
else
:
json_choice
=
field
.
to_json
(
json_choice
)
return
json_choice
metadata_fields
=
{}
metadata_fields
=
{}
# Only use the fields from this class, not mixins
# Only use the fields from this class, not mixins
...
@@ -688,56 +685,35 @@ class XModuleDescriptor(XModuleFields, HTMLSnippet, ResourceTemplates, XBlock):
...
@@ -688,56 +685,35 @@ class XModuleDescriptor(XModuleFields, HTMLSnippet, ResourceTemplates, XBlock):
if
field
.
scope
!=
Scope
.
settings
or
field
in
self
.
non_editable_metadata_fields
:
if
field
.
scope
!=
Scope
.
settings
or
field
in
self
.
non_editable_metadata_fields
:
continue
continue
inheritable
=
False
# gets the 'default_value' and 'explicitly_set' attrs
value
=
getattr
(
self
,
field
.
name
)
metadata_fields
[
field
.
name
]
=
self
.
runtime
.
get_field_provenance
(
self
,
field
)
default_value
=
field
.
default
metadata_fields
[
field
.
name
][
'field_name'
]
=
field
.
name
explicitly_set
=
self
.
_field_data
.
has
(
self
,
field
.
name
)
metadata_fields
[
field
.
name
][
'display_name'
]
=
field
.
display_name
if
field
.
name
in
inheritable_metadata
:
metadata_fields
[
field
.
name
][
'help'
]
=
field
.
help
inheritable
=
True
metadata_fields
[
field
.
name
][
'value'
]
=
field
.
read_json
(
self
)
default_value
=
field
.
from_json
(
inheritable_metadata
.
get
(
field
.
name
))
if
field
.
name
in
inherited_metadata
:
explicitly_set
=
False
# We support the following editors:
# We support the following editors:
# 1. A select editor for fields with a list of possible values (includes Booleans).
# 1. A select editor for fields with a list of possible values (includes Booleans).
# 2. Number editors for integers and floats.
# 2. Number editors for integers and floats.
# 3. A generic string editor for anything else (editing JSON representation of the value).
# 3. A generic string editor for anything else (editing JSON representation of the value).
editor_type
=
"Generic"
editor_type
=
"Generic"
values
=
copy
.
deepcopy
(
field
.
values
)
values
=
field
.
values
if
isinstance
(
values
,
tuple
):
if
isinstance
(
values
,
(
tuple
,
list
))
and
len
(
values
)
>
0
:
values
=
list
(
values
)
editor_type
=
"Select"
if
isinstance
(
values
,
list
):
values
=
[
jsonify_value
(
field
,
json_choice
)
for
json_choice
in
values
]
if
len
(
values
)
>
0
:
editor_type
=
"Select"
for
index
,
choice
in
enumerate
(
values
):
json_choice
=
copy
.
deepcopy
(
choice
)
if
isinstance
(
json_choice
,
dict
)
and
'value'
in
json_choice
:
json_choice
[
'value'
]
=
field
.
to_json
(
json_choice
[
'value'
])
else
:
json_choice
=
field
.
to_json
(
json_choice
)
values
[
index
]
=
json_choice
elif
isinstance
(
field
,
Integer
):
elif
isinstance
(
field
,
Integer
):
editor_type
=
"Integer"
editor_type
=
"Integer"
elif
isinstance
(
field
,
Float
):
elif
isinstance
(
field
,
Float
):
editor_type
=
"Float"
editor_type
=
"Float"
elif
isinstance
(
field
,
List
):
elif
isinstance
(
field
,
List
):
editor_type
=
"List"
editor_type
=
"List"
metadata_fields
[
field
.
name
]
=
{
metadata_fields
[
field
.
name
][
'type'
]
=
editor_type
'field_name'
:
field
.
name
,
metadata_fields
[
field
.
name
][
'options'
]
=
[]
if
values
is
None
else
values
'type'
:
editor_type
,
'display_name'
:
field
.
display_name
,
'value'
:
field
.
to_json
(
value
),
'options'
:
[]
if
values
is
None
else
values
,
'default_value'
:
field
.
to_json
(
default_value
),
'inheritable'
:
inheritable
,
'explicitly_set'
:
explicitly_set
,
'help'
:
field
.
help
,
}
return
metadata_fields
return
metadata_fields
# ~~~~~~~~~~~~~~~ XBlock API Wrappers ~~~~~~~~~~~~~~~~
# ~~~~~~~~~~~~~~~ XBlock API Wrappers ~~~~~~~~~~~~~~~~
def
studio_view
(
self
,
context
):
def
studio_view
(
self
,
_
context
):
"""
"""
Return a fragment with the html from this XModuleDescriptor's editing view
Return a fragment with the html from this XModuleDescriptor's editing view
...
@@ -750,6 +726,7 @@ class XModuleDescriptor(XModuleFields, HTMLSnippet, ResourceTemplates, XBlock):
...
@@ -750,6 +726,7 @@ class XModuleDescriptor(XModuleFields, HTMLSnippet, ResourceTemplates, XBlock):
class
DescriptorSystem
(
Runtime
):
class
DescriptorSystem
(
Runtime
):
def
__init__
(
self
,
load_item
,
resources_fs
,
error_tracker
,
**
kwargs
):
def
__init__
(
self
,
load_item
,
resources_fs
,
error_tracker
,
**
kwargs
):
"""
"""
load_item: Takes a Location and returns an XModuleDescriptor
load_item: Takes a Location and returns an XModuleDescriptor
...
@@ -797,6 +774,33 @@ class DescriptorSystem(Runtime):
...
@@ -797,6 +774,33 @@ class DescriptorSystem(Runtime):
"""See documentation for `xblock.runtime:Runtime.get_block`"""
"""See documentation for `xblock.runtime:Runtime.get_block`"""
return
self
.
load_item
(
block_id
)
return
self
.
load_item
(
block_id
)
def
get_field_provenance
(
self
,
xblock
,
field
):
"""
For the given xblock, return a dict for the field's current state:
{
'default_value': what json'd value will take effect if field is unset: either the field default or
inherited value,
'explicitly_set': boolean for whether the current value is set v default/inherited,
}
:param xblock:
:param field:
"""
# in runtime b/c runtime contains app-specific xblock behavior. Studio's the only app
# which needs this level of introspection right now. runtime also is 'allowed' to know
# about the kvs, dbmodel, etc.
result
=
{}
result
[
'explicitly_set'
]
=
xblock
.
_field_data
.
has
(
xblock
,
field
.
name
)
try
:
block_inherited
=
xblock
.
xblock_kvs
.
inherited_settings
except
AttributeError
:
# if inherited_settings doesn't exist on kvs
block_inherited
=
{}
if
field
.
name
in
block_inherited
:
result
[
'default_value'
]
=
block_inherited
[
field
.
name
]
else
:
result
[
'default_value'
]
=
field
.
to_json
(
field
.
default
)
return
result
class
XMLParsingSystem
(
DescriptorSystem
):
class
XMLParsingSystem
(
DescriptorSystem
):
def
__init__
(
self
,
process_xml
,
policy
,
**
kwargs
):
def
__init__
(
self
,
process_xml
,
policy
,
**
kwargs
):
...
...
common/lib/xmodule/xmodule/xml_module.py
View file @
fc131fa8
...
@@ -9,10 +9,9 @@ from lxml import etree
...
@@ -9,10 +9,9 @@ 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
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.field_data
import
DictFieldData
log
=
logging
.
getLogger
(
__name__
)
log
=
logging
.
getLogger
(
__name__
)
...
@@ -365,10 +364,12 @@ class XmlDescriptor(XModuleDescriptor):
...
@@ -365,10 +364,12 @@ class XmlDescriptor(XModuleDescriptor):
field_data
[
'xml_attributes'
][
key
]
=
value
field_data
[
'xml_attributes'
][
key
]
=
value
field_data
[
'location'
]
=
location
field_data
[
'location'
]
=
location
field_data
[
'category'
]
=
xml_object
.
tag
field_data
[
'category'
]
=
xml_object
.
tag
kvs
=
InheritanceKeyValueStore
(
initial_values
=
field_data
)
field_data
=
DbModel
(
kvs
)
return
system
.
construct_xblock_from_class
(
return
system
.
construct_xblock_from_class
(
cls
,
cls
,
DictFieldData
(
field_data
)
,
field_data
,
# 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,
...
...
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