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
63f34f2e
Commit
63f34f2e
authored
Jul 25, 2012
by
Victor Shnayder
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Line length and doc string cleanups
* no functionality changed in this commit.
parent
d43831e1
Show whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
260 additions
and
144 deletions
+260
-144
common/lib/xmodule/xmodule/backcompat_module.py
+12
-8
common/lib/xmodule/xmodule/capa_module.py
+24
-11
common/lib/xmodule/xmodule/course_module.py
+6
-5
common/lib/xmodule/xmodule/mako_module.py
+5
-2
common/lib/xmodule/xmodule/modulestore/__init__.py
+31
-18
common/lib/xmodule/xmodule/raw_module.py
+1
-1
common/lib/xmodule/xmodule/seq_module.py
+10
-5
common/lib/xmodule/xmodule/x_module.py
+147
-84
common/lib/xmodule/xmodule/xml_module.py
+24
-10
No files found.
common/lib/xmodule/xmodule/backcompat_module.py
View file @
63f34f2e
...
...
@@ -12,8 +12,8 @@ log = logging.getLogger(__name__)
def
process_includes
(
fn
):
"""
Wraps a XModuleDescriptor.from_xml method, and modifies xml_data to replace
any immediate child <include> items with the contents of the file that they
are
supposed to include
any immediate child <include> items with the contents of the file that they
are
supposed to include
"""
@wraps
(
fn
)
def
from_xml
(
cls
,
xml_data
,
system
,
org
=
None
,
course
=
None
):
...
...
@@ -25,15 +25,19 @@ def process_includes(fn):
try
:
ifp
=
system
.
resources_fs
.
open
(
file
)
except
Exception
:
log
.
exception
(
'Error in problem xml include:
%
s'
%
(
etree
.
tostring
(
next_include
,
pretty_print
=
True
)))
log
.
exception
(
'Cannot find file
%
s in
%
s'
%
(
file
,
dir
))
msg
=
'Error in problem xml include:
%
s
\n
'
%
(
etree
.
tostring
(
next_include
,
pretty_print
=
True
))
msg
+=
'Cannot find file
%
s in
%
s'
%
(
file
,
dir
)
log
.
exception
(
msg
)
raise
try
:
# read in and convert to XML
incxml
=
etree
.
XML
(
ifp
.
read
())
except
Exception
:
log
.
exception
(
'Error in problem xml include:
%
s'
%
(
etree
.
tostring
(
next_include
,
pretty_print
=
True
)))
log
.
exception
(
'Cannot parse XML in
%
s'
%
(
file
))
msg
=
'Error in problem xml include:
%
s
\n
'
%
(
etree
.
tostring
(
next_include
,
pretty_print
=
True
))
msg
+=
'Cannot parse XML in
%
s'
%
(
file
)
log
.
exception
(
msg
)
raise
# insert new XML into tree in place of inlcude
parent
=
next_include
.
getparent
()
...
...
@@ -50,8 +54,8 @@ class SemanticSectionDescriptor(XModuleDescriptor):
@process_includes
def
from_xml
(
cls
,
xml_data
,
system
,
org
=
None
,
course
=
None
):
"""
Removes sections
single child elements in favor of just embedding the child element
Removes sections
with single child elements in favor of just embedding
the child element
"""
xml_object
=
etree
.
fromstring
(
xml_data
)
...
...
common/lib/xmodule/xmodule/capa_module.py
View file @
63f34f2e
...
...
@@ -67,7 +67,8 @@ class ComplexEncoder(json.JSONEncoder):
class
CapaModule
(
XModule
):
'''
An XModule implementing LonCapa format problems, implemented by way of capa.capa_problem.LoncapaProblem
An XModule implementing LonCapa format problems, implemented by way of
capa.capa_problem.LoncapaProblem
'''
icon_class
=
'problem'
...
...
@@ -77,8 +78,10 @@ class CapaModule(XModule):
js_module_name
=
"Problem"
css
=
{
'scss'
:
[
resource_string
(
__name__
,
'css/capa/display.scss'
)]}
def
__init__
(
self
,
system
,
location
,
definition
,
instance_state
=
None
,
shared_state
=
None
,
**
kwargs
):
XModule
.
__init__
(
self
,
system
,
location
,
definition
,
instance_state
,
shared_state
,
**
kwargs
)
def
__init__
(
self
,
system
,
location
,
definition
,
instance_state
=
None
,
shared_state
=
None
,
**
kwargs
):
XModule
.
__init__
(
self
,
system
,
location
,
definition
,
instance_state
,
shared_state
,
**
kwargs
)
self
.
attempts
=
0
self
.
max_attempts
=
None
...
...
@@ -133,7 +136,8 @@ class CapaModule(XModule):
seed
=
None
try
:
self
.
lcp
=
LoncapaProblem
(
self
.
definition
[
'data'
],
self
.
location
.
html_id
(),
instance_state
,
seed
=
seed
,
system
=
self
.
system
)
self
.
lcp
=
LoncapaProblem
(
self
.
definition
[
'data'
],
self
.
location
.
html_id
(),
instance_state
,
seed
=
seed
,
system
=
self
.
system
)
except
Exception
:
msg
=
'cannot create LoncapaProblem
%
s'
%
self
.
location
.
url
()
log
.
exception
(
msg
)
...
...
@@ -141,15 +145,20 @@ class CapaModule(XModule):
msg
=
'<p>
%
s</p>'
%
msg
.
replace
(
'<'
,
'<'
)
msg
+=
'<p><pre>
%
s</pre></p>'
%
traceback
.
format_exc
()
.
replace
(
'<'
,
'<'
)
# create a dummy problem with error message instead of failing
problem_text
=
'<problem><text><font color="red" size="+2">Problem
%
s has an error:</font>
%
s</text></problem>'
%
(
self
.
location
.
url
(),
msg
)
self
.
lcp
=
LoncapaProblem
(
problem_text
,
self
.
location
.
html_id
(),
instance_state
,
seed
=
seed
,
system
=
self
.
system
)
problem_text
=
(
'<problem><text><font color="red" size="+2">'
'Problem
%
s has an error:</font>
%
s</text></problem>'
%
(
self
.
location
.
url
(),
msg
))
self
.
lcp
=
LoncapaProblem
(
problem_text
,
self
.
location
.
html_id
(),
instance_state
,
seed
=
seed
,
system
=
self
.
system
)
else
:
raise
@property
def
rerandomize
(
self
):
"""
Property accessor that returns self.metadata['rerandomize'] in a canonical form
Property accessor that returns self.metadata['rerandomize'] in a
canonical form
"""
rerandomize
=
self
.
metadata
.
get
(
'rerandomize'
,
'always'
)
if
rerandomize
in
(
""
,
"always"
,
"true"
):
...
...
@@ -203,7 +212,10 @@ class CapaModule(XModule):
except
Exception
,
err
:
if
self
.
system
.
DEBUG
:
log
.
exception
(
err
)
msg
=
'[courseware.capa.capa_module] <font size="+1" color="red">Failed to generate HTML for problem
%
s</font>'
%
(
self
.
location
.
url
())
msg
=
(
'[courseware.capa.capa_module] <font size="+1" color="red">'
'Failed to generate HTML for problem
%
s</font>'
%
(
self
.
location
.
url
()))
msg
+=
'<p>Error:</p><p><pre>
%
s</pre></p>'
%
str
(
err
)
.
replace
(
'<'
,
'<'
)
msg
+=
'<p><pre>
%
s</pre></p>'
%
traceback
.
format_exc
()
.
replace
(
'<'
,
'<'
)
html
=
msg
...
...
@@ -215,8 +227,8 @@ class CapaModule(XModule):
'weight'
:
self
.
weight
,
}
# We using strings as truthy values, because the terminology of the
check button
# is context-specific.
# We using strings as truthy values, because the terminology of the
#
check button
is context-specific.
check_button
=
"Grade"
if
self
.
max_attempts
else
"Check"
reset_button
=
True
save_button
=
True
...
...
@@ -242,7 +254,8 @@ class CapaModule(XModule):
if
not
self
.
lcp
.
done
:
reset_button
=
False
# We don't need a "save" button if infinite number of attempts and non-randomized
# We don't need a "save" button if infinite number of attempts and
# non-randomized
if
self
.
max_attempts
is
None
and
self
.
rerandomize
!=
"always"
:
save_button
=
False
...
...
common/lib/xmodule/xmodule/course_module.py
View file @
63f34f2e
...
...
@@ -17,11 +17,12 @@ class CourseDescriptor(SequenceDescriptor):
try
:
self
.
start
=
time
.
strptime
(
self
.
metadata
[
"start"
],
"
%
Y-
%
m-
%
dT
%
H:
%
M"
)
except
KeyError
:
self
.
start
=
time
.
gmtime
(
0
)
# The epoch
log
.
critical
(
"Course loaded without a start date. "
+
str
(
self
.
id
))
except
ValueError
,
e
:
self
.
start
=
time
.
gmtime
(
0
)
# The epoch
log
.
critical
(
"Course loaded with a bad start date. "
+
str
(
self
.
id
)
+
" '"
+
str
(
e
)
+
"'"
)
self
.
start
=
time
.
gmtime
(
0
)
#The epoch
log
.
critical
(
"Course loaded without a start date.
%
s"
,
self
.
id
)
except
ValueError
as
e
:
self
.
start
=
time
.
gmtime
(
0
)
#The epoch
log
.
critical
(
"Course loaded with a bad start date.
%
s '
%
s'"
,
self
.
id
,
e
)
def
has_started
(
self
):
return
time
.
gmtime
()
>
self
.
start
...
...
common/lib/xmodule/xmodule/mako_module.py
View file @
63f34f2e
...
...
@@ -19,7 +19,9 @@ class MakoModuleDescriptor(XModuleDescriptor):
def
__init__
(
self
,
system
,
definition
=
None
,
**
kwargs
):
if
getattr
(
system
,
'render_template'
,
None
)
is
None
:
raise
TypeError
(
'{system} must have a render_template function in order to use a MakoDescriptor'
.
format
(
system
=
system
))
raise
TypeError
(
'{system} must have a render_template function'
' in order to use a MakoDescriptor'
.
format
(
system
=
system
))
super
(
MakoModuleDescriptor
,
self
)
.
__init__
(
system
,
definition
,
**
kwargs
)
def
get_context
(
self
):
...
...
@@ -29,4 +31,5 @@ class MakoModuleDescriptor(XModuleDescriptor):
return
{
'module'
:
self
}
def
get_html
(
self
):
return
self
.
system
.
render_template
(
self
.
mako_template
,
self
.
get_context
())
return
self
.
system
.
render_template
(
self
.
mako_template
,
self
.
get_context
())
common/lib/xmodule/xmodule/modulestore/__init__.py
View file @
63f34f2e
...
...
@@ -45,13 +45,17 @@ class Location(_LocationBase):
"""
return
re
.
sub
(
'_+'
,
'_'
,
INVALID_CHARS
.
sub
(
'_'
,
value
))
def
__new__
(
_cls
,
loc_or_tag
=
None
,
org
=
None
,
course
=
None
,
category
=
None
,
name
=
None
,
revision
=
None
):
def
__new__
(
_cls
,
loc_or_tag
=
None
,
org
=
None
,
course
=
None
,
category
=
None
,
name
=
None
,
revision
=
None
):
"""
Create a new location that is a clone of the specifed one.
location - Can be any of the following types:
string: should be of the form {tag}://{org}/{course}/{category}/{name}[/{revision}]
string: should be of the form
{tag}://{org}/{course}/{category}/{name}[/{revision}]
list: should be of the form [tag, org, course, category, name, revision]
dict: should be of the form {
'tag': tag,
'org': org,
...
...
@@ -62,16 +66,19 @@ class Location(_LocationBase):
}
Location: another Location object
In both the dict and list forms, the revision is optional, and can be ommitted.
In both the dict and list forms, the revision is optional, and can be
ommitted.
Components must be composed of alphanumeric characters, or the characters '_', '-', and '.'
Components must be composed of alphanumeric characters, or the
characters '_', '-', and '.'
Components may be set to None, which may be interpreted by some contexts
to mean
wildcard selection
Components may be set to None, which may be interpreted by some contexts
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
else
:
location
=
(
loc_or_tag
,
org
,
course
,
category
,
name
,
revision
)
...
...
@@ -131,9 +138,11 @@ class Location(_LocationBase):
def
html_id
(
self
):
"""
Return a string with a version of the location that is safe for use in html id attributes
Return a string with a version of the location that is safe for use in
html id attributes
"""
return
"-"
.
join
(
str
(
v
)
for
v
in
self
.
list
()
if
v
is
not
None
)
.
replace
(
'.'
,
'_'
)
return
"-"
.
join
(
str
(
v
)
for
v
in
self
.
list
()
if
v
is
not
None
)
.
replace
(
'.'
,
'_'
)
def
dict
(
self
):
"""
...
...
@@ -154,7 +163,8 @@ class Location(_LocationBase):
class
ModuleStore
(
object
):
"""
An abstract interface for a database backend that stores XModuleDescriptor instances
An abstract interface for a database backend that stores XModuleDescriptor
instances
"""
def
get_item
(
self
,
location
,
depth
=
0
):
"""
...
...
@@ -164,13 +174,16 @@ class ModuleStore(object):
If any segment of the location is None except revision, raises
xmodule.modulestore.exceptions.InsufficientSpecificationError
If no object is found at that location, raises xmodule.modulestore.exceptions.ItemNotFoundError
If no object is found at that location, raises
xmodule.modulestore.exceptions.ItemNotFoundError
location: Something that can be passed to Location
depth (int): An argument that some module stores may use to prefetch descendents of the queried modules
for more efficient results later in the request. The depth is counted in the number of
calls to get_children() to cache. None indicates to cache all descendents
depth (int): An argument that some module stores may use to prefetch
descendents of the queried modules for more efficient results later
in the request. The depth is counted in the number of calls to
get_children() to cache. None indicates to cache all descendents
"""
raise
NotImplementedError
...
...
@@ -182,9 +195,10 @@ class ModuleStore(object):
location: Something that can be passed to Location
depth: An argument that some module stores may use to prefetch descendents of the queried modules
for more efficient results later in the request. The depth is counted in the number of calls
to get_children() to cache. None indicates to cache all descendents
depth: An argument that some module stores may use to prefetch
descendents of the queried modules for more efficient results later
in the request. The depth is counted in the number of calls to
get_children() to cache. None indicates to cache all descendents
"""
raise
NotImplementedError
...
...
@@ -228,4 +242,3 @@ class ModuleStore(object):
in this modulestore.
'''
raise
NotImplementedError
common/lib/xmodule/xmodule/raw_module.py
View file @
63f34f2e
...
...
@@ -8,7 +8,7 @@ log = logging.getLogger(__name__)
class
RawDescriptor
(
MakoModuleDescriptor
,
XmlDescriptor
):
"""
Module that provides a raw editing view of it
'
s data and children
Module that provides a raw editing view of its data and children
"""
mako_template
=
"widgets/raw-edit.html"
...
...
common/lib/xmodule/xmodule/seq_module.py
View file @
63f34f2e
...
...
@@ -20,12 +20,15 @@ class_priority = ['video', 'problem']
class
SequenceModule
(
XModule
):
''' Layout module which lays out content in a temporal sequence
'''
js
=
{
'coffee'
:
[
resource_string
(
__name__
,
'js/src/sequence/display.coffee'
)]}
js
=
{
'coffee'
:
[
resource_string
(
__name__
,
'js/src/sequence/display.coffee'
)]}
css
=
{
'scss'
:
[
resource_string
(
__name__
,
'css/sequence/display.scss'
)]}
js_module_name
=
"Sequence"
def
__init__
(
self
,
system
,
location
,
definition
,
instance_state
=
None
,
shared_state
=
None
,
**
kwargs
):
XModule
.
__init__
(
self
,
system
,
location
,
definition
,
instance_state
,
shared_state
,
**
kwargs
)
def
__init__
(
self
,
system
,
location
,
definition
,
instance_state
=
None
,
shared_state
=
None
,
**
kwargs
):
XModule
.
__init__
(
self
,
system
,
location
,
definition
,
instance_state
,
shared_state
,
**
kwargs
)
self
.
position
=
1
if
instance_state
is
not
None
:
...
...
@@ -92,7 +95,8 @@ class SequenceModule(XModule):
self
.
rendered
=
True
def
get_icon_class
(
self
):
child_classes
=
set
(
child
.
get_icon_class
()
for
child
in
self
.
get_children
())
child_classes
=
set
(
child
.
get_icon_class
()
for
child
in
self
.
get_children
())
new_class
=
'other'
for
c
in
class_priority
:
if
c
in
child_classes
:
...
...
@@ -114,5 +118,6 @@ class SequenceDescriptor(MakoModuleDescriptor, XmlDescriptor):
def
definition_to_xml
(
self
,
resource_fs
):
xml_object
=
etree
.
Element
(
'sequential'
)
for
child
in
self
.
get_children
():
xml_object
.
append
(
etree
.
fromstring
(
child
.
export_to_xml
(
resource_fs
)))
xml_object
.
append
(
etree
.
fromstring
(
child
.
export_to_xml
(
resource_fs
)))
return
xml_object
common/lib/xmodule/xmodule/x_module.py
View file @
63f34f2e
...
...
@@ -31,23 +31,28 @@ class Plugin(object):
def
load_class
(
cls
,
identifier
,
default
=
None
):
"""
Loads a single class instance specified by identifier. If identifier
specifies more than a single class, then logs a warning and returns the
first
class identified.
specifies more than a single class, then logs a warning and returns the
first
class identified.
If default is not None, will return default if no entry_point matching
identifier
is found. Otherwise, will raise a ModuleMissingError
If default is not None, will return default if no entry_point matching
i
dentifier i
s found. Otherwise, will raise a ModuleMissingError
"""
if
cls
.
_plugin_cache
is
None
:
cls
.
_plugin_cache
=
{}
if
identifier
not
in
cls
.
_plugin_cache
:
identifier
=
identifier
.
lower
()
classes
=
list
(
pkg_resources
.
iter_entry_points
(
cls
.
entry_point
,
name
=
identifier
))
classes
=
list
(
pkg_resources
.
iter_entry_points
(
cls
.
entry_point
,
name
=
identifier
))
if
len
(
classes
)
>
1
:
log
.
warning
(
"Found multiple classes for {entry_point} with identifier {id}: {classes}. Returning the first one."
.
format
(
log
.
warning
(
"Found multiple classes for {entry_point} with "
"identifier {id}: {classes}. "
"Returning the first one."
.
format
(
entry_point
=
cls
.
entry_point
,
id
=
identifier
,
classes
=
", "
.
join
(
class_
.
module_name
for
class_
in
classes
)))
classes
=
", "
.
join
(
class_
.
module_name
for
class_
in
classes
)))
if
len
(
classes
)
==
0
:
if
default
is
not
None
:
...
...
@@ -79,9 +84,12 @@ class HTMLSnippet(object):
def
get_javascript
(
cls
):
"""
Return a dictionary containing some of the following keys:
coffee: A list of coffeescript fragments that should be compiled and
placed on the page
js: A list of javascript fragments that should be included on the page
js: A list of javascript fragments that should be included on the
page
All of these will be loaded onto the page in the CMS
"""
...
...
@@ -91,12 +99,15 @@ class HTMLSnippet(object):
def
get_css
(
cls
):
"""
Return a dictionary containing some of the following keys:
css: A list of css fragments that should be applied to the html contents
of the snippet
sass: A list of sass fragments that should be applied to the html contents
of the snippet
scss: A list of scss fragments that should be applied to the html contents
of the snippet
css: A list of css fragments that should be applied to the html
contents of the snippet
sass: A list of sass fragments that should be applied to the html
contents of the snippet
scss: A list of scss fragments that should be applied to the html
contents of the snippet
"""
return
cls
.
css
...
...
@@ -104,47 +115,70 @@ class HTMLSnippet(object):
"""
Return the html used to display this snippet
"""
raise
NotImplementedError
(
"get_html() must be provided by specific modules - not present in {0}"
raise
NotImplementedError
(
"get_html() must be provided by specific modules - not present in {0}"
.
format
(
self
.
__class__
))
class
XModule
(
HTMLSnippet
):
''' Implements a generic learning module.
Subclasses must at a minimum provide a definition for get_html in order to be displayed to users.
Subclasses must at a minimum provide a definition for get_html in order
to be displayed to users.
See the HTML module for a simple example.
'''
# The default implementation of get_icon_class returns the icon_class attribute of the class
# This attribute can be overridden by subclasses, and the function can also be overridden
# if the icon class depends on the data in the module
# The default implementation of get_icon_class returns the icon_class
# attribute of the class
#
# This attribute can be overridden by subclasses, and
# the function can also be overridden if the icon class depends on the data
# in the module
icon_class
=
'other'
def
__init__
(
self
,
system
,
location
,
definition
,
instance_state
=
None
,
shared_state
=
None
,
**
kwargs
):
def
__init__
(
self
,
system
,
location
,
definition
,
instance_state
=
None
,
shared_state
=
None
,
**
kwargs
):
'''
Construct a new xmodule
system: A ModuleSystem allowing access to external resources
location: Something Location-like that identifies this xmodule
definition: A dictionary containing 'data' and 'children'. Both are optional
'data': is JSON-like (string, dictionary, list, bool, or None, optionally nested).
This defines all of the data necessary for a problem to display that is intrinsic to the problem.
It should not include any data that would vary between two courses using the same problem
definition: A dictionary containing 'data' and 'children'. Both are
optional
'data': is JSON-like (string, dictionary, list, bool, or None,
optionally nested).
This defines all of the data necessary for a problem to display
that is intrinsic to the problem. It should not include any
data that would vary between two courses using the same problem
(due dates, grading policy, randomization, etc.)
'children': is a list of Location-like values for child modules that this module depends on
instance_state: A string of serialized json that contains the state of this module for
current student accessing the system, or None if no state has been saved
shared_state: A string of serialized json that contains the state that is shared between
this module and any modules of the same type with the same shared_state_key. This
state is only shared per-student, not across different students
kwargs: Optional arguments. Subclasses should always accept kwargs and pass them
to the parent class constructor.
'children': is a list of Location-like values for child modules that
this module depends on
instance_state: A string of serialized json that contains the state of
this module for current student accessing the system, or None if
no state has been saved
shared_state: A string of serialized json that contains the state that
is shared between this module and any modules of the same type with
the same shared_state_key. This state is only shared per-student,
not across different students
kwargs: Optional arguments. Subclasses should always accept kwargs and
pass them to the parent class constructor.
Current known uses of kwargs:
metadata: SCAFFOLDING - This dictionary will be split into several different types of metadata
in the future (course policy, modification history, etc).
A dictionary containing data that specifies information that is particular
to a problem in the context of a course
metadata: SCAFFOLDING - This dictionary will be split into
several different types of metadata in the future (course
policy, modification history, etc). A dictionary containing
data that specifies information that is particular to a
problem in the context of a course
'''
self
.
system
=
system
self
.
location
=
Location
(
location
)
...
...
@@ -217,16 +251,21 @@ class XModule(HTMLSnippet):
def
max_score
(
self
):
''' Maximum score. Two notes:
* This is generic; in abstract, a problem could be 3/5 points on one randomization, and 5/7 on another
* In practice, this is a Very Bad Idea, and (a) will break some code in place (although that code
should get fixed), and (b) break some analytics we plan to put in place.
* This is generic; in abstract, a problem could be 3/5 points on one
randomization, and 5/7 on another
* In practice, this is a Very Bad Idea, and (a) will break some code
in place (although that code should get fixed), and (b) break some
analytics we plan to put in place.
'''
return
None
def
get_progress
(
self
):
''' Return a progress.Progress object that represents how far the student has gone
in this module. Must be implemented to get correct progress tracking behavior in
nesting modules like sequence and vertical.
''' Return a progress.Progress object that represents how far the
student has gone in this module. Must be implemented to get correct
progress tracking behavior in nesting modules like sequence and
vertical.
If this module has no notion of progress, return None.
'''
...
...
@@ -240,13 +279,14 @@ class XModule(HTMLSnippet):
class
XModuleDescriptor
(
Plugin
,
HTMLSnippet
):
"""
An XModuleDescriptor is a specification for an element of a course. This
could
be a problem, an organizational element (a group of content), or a segment of video,
for example.
An XModuleDescriptor is a specification for an element of a course. This
could be a problem, an organizational element (a group of content), or a
segment of video,
for example.
XModuleDescriptors are independent and agnostic to the current student state on a
problem. They handle the editing interface used by instructors to create a problem,
and can generate XModules (which do know about student state).
XModuleDescriptors are independent and agnostic to the current student state
on a problem. They handle the editing interface used by instructors to
create a problem, and can generate XModules (which do know about student
state).
"""
entry_point
=
"xmodule.v1"
module_class
=
XModule
...
...
@@ -255,46 +295,58 @@ class XModuleDescriptor(Plugin, HTMLSnippet):
inheritable_metadata
=
(
'graded'
,
'start'
,
'due'
,
'graceperiod'
,
'showanswer'
,
'rerandomize'
,
# T
his is used by the XMLModuleStore to provide for locations for static files,
# and will need to be removed when that code is removed
# T
ODO: This is used by the XMLModuleStore to provide for locations for
#
static files,
and will need to be removed when that code is removed
'data_dir'
)
# A list of descriptor attributes that must be equal for the descriptors to be
# equal
equality_attributes
=
(
'definition'
,
'metadata'
,
'location'
,
'shared_state_key'
,
'_inherited_metadata'
)
# A list of descriptor attributes that must be equal for the descriptors to
# be equal
equality_attributes
=
(
'definition'
,
'metadata'
,
'location'
,
'shared_state_key'
,
'_inherited_metadata'
)
# ============================= STRUCTURAL MANIPULATION ===================
========
# ============================= STRUCTURAL MANIPULATION ===================
def
__init__
(
self
,
system
,
definition
=
None
,
**
kwargs
):
"""
Construct a new XModuleDescriptor. The only required arguments are the
system, used for interaction with external resources, and the definition,
which specifies all the data needed to edit and display the problem (but none
of the associated metadata that handles recordkeeping around the problem).
system, used for interaction with external resources, and the
definition, which specifies all the data needed to edit and display the
problem (but none of the associated metadata that handles recordkeeping
around the problem).
This allows for maximal flexibility to add to the interface while
preserving backwards compatibility.
This allows for maximal flexibility to add to the interface while preserving
backwards compatibility.
system: A DescriptorSystem for interacting with external resources
system: An XModuleSystem for interacting with external resources
definition: A dict containing `data` and `children` representing the
problem definition
definition: A dict containing `data` and `children` representing the
problem definition
Current arguments passed in kwargs:
location: A xmodule.modulestore.Location object indicating the name and ownership of this problem
shared_state_key: The key to use for sharing StudentModules with other
modules of this type
location: A xmodule.modulestore.Location object indicating the name
and ownership of this problem
shared_state_key: The key to use for sharing StudentModules with
other modules of this type
metadata: A dictionary containing the following optional keys:
goals: A list of strings of learning goals associated with this module
display_name: The name to use for displaying this module to the user
goals: A list of strings of learning goals associated with this
module
display_name: The name to use for displaying this module to the
user
format: The format of this module ('Homework', 'Lab', etc)
graded (bool): Whether this module is should be graded or not
start (string): The date for which this module will be available
due (string): The due date for this module
graceperiod (string): The amount of grace period to allow when enforcing the due date
graceperiod (string): The amount of grace period to allow when
enforcing the due date
showanswer (string): When to show answers for this module
rerandomize (string): When to generate a newly randomized instance of the module data
rerandomize (string): When to generate a newly randomized
instance of the module data
"""
self
.
system
=
system
self
.
metadata
=
kwargs
.
get
(
'metadata'
,
{})
...
...
@@ -321,7 +373,8 @@ class XModuleDescriptor(Plugin, HTMLSnippet):
self
.
metadata
[
attr
]
=
metadata
[
attr
]
def
get_children
(
self
):
"""Returns a list of XModuleDescriptor instances for the children of this module"""
"""Returns a list of XModuleDescriptor instances for the children of
this module"""
if
self
.
_child_instances
is
None
:
self
.
_child_instances
=
[]
for
child_loc
in
self
.
definition
.
get
(
'children'
,
[]):
...
...
@@ -333,8 +386,9 @@ class XModuleDescriptor(Plugin, HTMLSnippet):
def
xmodule_constructor
(
self
,
system
):
"""
Returns a constructor for an XModule. This constructor takes two arguments:
instance_state and shared_state, and returns a fully nstantiated XModule
Returns a constructor for an XModule. This constructor takes two
arguments: instance_state and shared_state, and returns a fully
instantiated XModule
"""
return
partial
(
self
.
module_class
,
...
...
@@ -344,7 +398,7 @@ class XModuleDescriptor(Plugin, HTMLSnippet):
metadata
=
self
.
metadata
)
# ================================= JSON PARSING ===========================
========
# ================================= JSON PARSING ===========================
@staticmethod
def
load_from_json
(
json_data
,
system
,
default_class
=
None
):
"""
...
...
@@ -366,13 +420,14 @@ class XModuleDescriptor(Plugin, HTMLSnippet):
Creates an instance of this descriptor from the supplied json_data.
This may be overridden by subclasses
json_data: A json object specifying the definition and any optional keyword arguments for
the XModuleDescriptor
system: An XModuleSystem for interacting with external resources
json_data: A json object specifying the definition and any optional
keyword arguments for the XModuleDescriptor
system: A DescriptorSystem for interacting with external resources
"""
return
cls
(
system
=
system
,
**
json_data
)
# ================================= XML PARSING ============================
========
# ================================= XML PARSING ============================
@staticmethod
def
load_from_xml
(
xml_data
,
system
,
...
...
@@ -487,24 +542,33 @@ class ModuleSystem(object):
'''
def
__init__
(
self
,
ajax_url
,
track_function
,
get_module
,
render_template
,
replace_urls
,
user
=
None
,
filestore
=
None
,
debug
=
False
,
xqueue_callback_url
=
None
):
user
=
None
,
filestore
=
None
,
debug
=
False
,
xqueue_callback_url
=
None
):
'''
Create a closure around the system environment.
ajax_url - the url where ajax calls to the encapsulating module go.
track_function - function of (event_type, event), intended for logging
or otherwise tracking the event.
TODO: Not used, and has inconsistent args in different
files. Update or remove.
get_module - function that takes (location) and returns a corresponding
module instance object.
render_template - a function that takes (template_file, context), and returns
rendered html.
user - The user to base the random number generator seed off of for this request
filestore - A filestore ojbect. Defaults to an instance of OSFS based at
settings.DATA_DIR.
render_template - a function that takes (template_file, context), and
returns rendered html.
user - The user to base the random number generator seed off of for this
request
filestore - A filestore ojbect. Defaults to an instance of OSFS based
at settings.DATA_DIR.
replace_urls - TEMPORARY - A function like static_replace.replace_urls
that capa_module can use to fix up the static urls in ajax results.
that capa_module can use to fix up the static urls in
ajax results.
'''
self
.
ajax_url
=
ajax_url
self
.
xqueue_callback_url
=
xqueue_callback_url
...
...
@@ -529,4 +593,3 @@ class ModuleSystem(object):
def
__str__
(
self
):
return
str
(
self
.
__dict__
)
common/lib/xmodule/xmodule/xml_module.py
View file @
63f34f2e
...
...
@@ -13,13 +13,19 @@ log = logging.getLogger(__name__)
# TODO (cpennington): This was implemented in an attempt to improve performance,
# but the actual improvement wasn't measured (and it was implemented late at night).
# We should check if it hurts, and whether there's a better way of doing lazy loading
class
LazyLoadingDict
(
MutableMapping
):
"""
A dictionary object that lazily loads it
'
s contents from a provided
function on reads (of members that haven't already been set)
A dictionary object that lazily loads its contents from a provided
function on reads (of members that haven't already been set)
.
"""
def
__init__
(
self
,
loader
):
'''
On the first read from this dictionary, it will call loader() to
populate its contents. loader() must return something dict-like. Any
elements set before the first read will be preserved.
'''
self
.
_contents
=
{}
self
.
_loaded
=
False
self
.
_loader
=
loader
...
...
@@ -70,10 +76,12 @@ _AttrMapBase = namedtuple('_AttrMap', 'metadata_key to_metadata from_metadata')
class
AttrMap
(
_AttrMapBase
):
"""
A class that specifies a metadata_key, a function to transform an xml
attribute to be placed in that key,
and to transform that key value
A class that specifies a metadata_key, a function to transform an xml
a
ttribute to be placed in that key, a
nd to transform that key value
"""
def
__new__
(
_cls
,
metadata_key
,
to_metadata
=
lambda
x
:
x
,
from_metadata
=
lambda
x
:
x
):
def
__new__
(
_cls
,
metadata_key
,
to_metadata
=
lambda
x
:
x
,
from_metadata
=
lambda
x
:
x
):
return
_AttrMapBase
.
__new__
(
_cls
,
metadata_key
,
to_metadata
,
from_metadata
)
...
...
@@ -93,7 +101,9 @@ class XmlDescriptor(XModuleDescriptor):
# A dictionary mapping xml attribute names to functions of the value
# that return the metadata key and value
xml_attribute_map
=
{
'graded'
:
AttrMap
(
'graded'
,
lambda
val
:
val
==
'true'
,
lambda
val
:
str
(
val
)
.
lower
()),
'graded'
:
AttrMap
(
'graded'
,
lambda
val
:
val
==
'true'
,
lambda
val
:
str
(
val
)
.
lower
()),
'name'
:
AttrMap
(
'display_name'
),
}
...
...
@@ -105,12 +115,14 @@ class XmlDescriptor(XModuleDescriptor):
xml_object: An etree Element
"""
raise
NotImplementedError
(
"
%
s does not implement definition_from_xml"
%
cls
.
__name__
)
raise
NotImplementedError
(
"
%
s does not implement definition_from_xml"
%
cls
.
__name__
)
@classmethod
def
clean_metadata_from_xml
(
cls
,
xml_object
):
"""
Remove any attribute named in self.metadata_attributes from the supplied xml_object
Remove any attribute named in cls.metadata_attributes from the supplied
xml_object
"""
for
attr
in
cls
.
metadata_attributes
:
if
xml_object
.
get
(
attr
)
is
not
None
:
...
...
@@ -134,7 +146,7 @@ class XmlDescriptor(XModuleDescriptor):
xml_data: A string of xml that will be translated into data and children for
this module
system: A
n XModule
System for interacting with external resources
system: A
Descriptor
System for interacting with external resources
org and course are optional strings that will be used in the generated modules
url identifiers
"""
...
...
@@ -157,6 +169,7 @@ class XmlDescriptor(XModuleDescriptor):
else
:
filepath
=
cls
.
_format_filepath
(
xml_object
.
tag
,
filename
)
# VS[compat]
# TODO (cpennington): If the file doesn't exist at the right path,
# give the class a chance to fix it up. The file will be written out again
# in the correct format.
...
...
@@ -243,4 +256,5 @@ class XmlDescriptor(XModuleDescriptor):
"""
Return a new etree Element object created from this modules definition.
"""
raise
NotImplementedError
(
"
%
s does not implement definition_to_xml"
%
self
.
__class__
.
__name__
)
raise
NotImplementedError
(
"
%
s does not implement definition_to_xml"
%
self
.
__class__
.
__name__
)
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