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
13efeaa5
Commit
13efeaa5
authored
Nov 16, 2012
by
Calen Pennington
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add demo of CascadeKeys policy working
parent
ea1100d4
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
211 additions
and
6 deletions
+211
-6
common/lib/xmodule/setup.py
+4
-0
common/lib/xmodule/xmodule/course_module.py
+159
-6
common/lib/xmodule/xmodule/structure_module.py
+48
-0
No files found.
common/lib/xmodule/setup.py
View file @
13efeaa5
...
...
@@ -41,6 +41,10 @@ setup(
],
'xmodule.v2'
:
[
'vertical = xmodule.vertical_module:VerticalModule'
,
'course = xmodule.course_module:CourseModule'
,
],
'policy.v1'
:
[
'cascade = xmodule.course_module:CascadeKeys'
,
]
}
)
common/lib/xmodule/xmodule/course_module.py
View file @
13efeaa5
from
fs.errors
import
ResourceNotFoundError
import
logging
from
lxml
import
etree
from
path
import
path
# NOTE (THK): Only used for detecting presence of syllabus
import
requests
import
time
import
hashlib
from
xmodule.util.decorators
import
lazyproperty
from
xmodule.graders
import
load_grading_policy
from
xmodule.modulestore
import
Location
from
xmodule.seq_module
import
SequenceDescriptor
,
SequenceModule
from
xmodule.timeparse
import
parse_time
,
stringify_time
from
.util.decorators
import
lazyproperty
from
.graders
import
load_grading_policy
from
.modulestore
import
Location
from
.seq_module
import
SequenceDescriptor
,
SequenceModule
from
.timeparse
import
parse_time
,
stringify_time
from
.structure_module
import
StructureModule
from
.xmodule
import
Plugin
log
=
logging
.
getLogger
(
__name__
)
def
load_policies
(
policy_list
):
"""
policy_list is a list of dictionaries, each with the following keys:
class: The name of a registered policy plugin
condition: An optional dictionary contaning the optional keys:
ids: A list of user ids for whom this policy should be applied
roles: A list of user roles for whom this policy should be applied
args: An option dictionary containing named arguments to pass to the policy plugin
"""
return
[
Policy
.
load_class
(
policy
[
'class'
])(
condition
=
policy
.
get
(
'condition'
),
**
policy
.
get
(
'params'
,
{}))
for
policy
in
policy_list
]
class
CourseModule
(
StructureModule
):
@property
def
policies
(
self
):
return
load_policies
(
self
.
content
.
get
(
'policy_list'
,
[]))
def
apply_policies
(
self
,
user
):
# N.B. this code needs to be expanded to handle policies that are
# time specific and thus return an expire header
policies_to_apply
=
[
policy
for
policy
in
self
.
policies
if
policy
.
applies_to
(
user
)
]
cache_key
=
self
.
cache_id
(
policies_to_apply
)
cached_tree
=
self
.
runtime
.
cache
(
'policy'
)
.
get
(
cache_key
)
if
cached_tree
is
not
None
:
return
cached_tree
tree
=
self
.
usage_tree
for
policy
in
policies_to_apply
:
tree
=
policy
.
apply
(
tree
)
self
.
runtime
.
cache
(
'policy'
)
.
set
(
cache_key
,
tree
)
return
tree
def
cache_id
(
self
,
policies
):
hasher
=
hashlib
.
md5
(
str
(
self
.
usage_tree
.
as_json
()))
for
policy
in
policies
:
hasher
.
update
(
policy
.
id
)
return
hasher
.
hexdigest
()
class
Policy
(
Plugin
):
entry_point
=
'policy.v1'
def
__init__
(
self
,
condition
):
self
.
condition
=
condition
self
.
id
=
str
(
id
(
self
))
def
apply
(
self
,
tree
):
return
tree
def
applies_to
(
self
,
user
):
if
self
.
condition
is
None
:
return
True
# N.B. This code may need to expand to allow a more expressive
# conditional language
applies_by_id
=
user
.
id
in
self
.
condition
.
get
(
'ids'
,
[])
applies_by_role
=
bool
(
set
(
user
.
groups
)
&
set
(
self
.
condition
.
get
(
'roles'
,
set
())))
return
applies_by_id
or
applies_by_role
class
CascadeKeys
(
Policy
):
"""
Policy that cascades the values specified for a set of policy keys
down the tree, prioritizing policies already set on descendents
over those being cascaded
"""
def
__init__
(
self
,
keys
,
*
args
,
**
kwargs
):
super
(
CascadeKeys
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
self
.
keys
=
keys
def
apply
(
self
,
tree
):
def
cascade
(
settings
):
new_settings
=
dict
(
settings
)
for
key
in
self
.
keys
:
if
key
not
in
new_settings
and
key
in
tree
.
settings
:
new_settings
[
key
]
=
tree
.
settings
[
key
]
return
new_settings
children
=
[
self
.
apply
(
child
.
_replace
(
settings
=
cascade
(
child
.
settings
)))
for
child
in
tree
.
children
]
return
tree
.
_replace
(
children
=
children
)
class
Reschedule
(
Policy
):
"""
This policy adds a specified timedelta to all start_dates
"""
def
__init__
(
self
,
delta
,
*
args
,
**
kwargs
):
super
(
CascadeKeys
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
self
.
delta
=
delta
def
apply
(
self
,
tree
):
children
=
[
self
.
apply
(
child
)
for
child
in
tree
.
children
]
settings
=
dict
(
tree
.
settings
)
if
'start_date'
in
policy
:
settings
[
'start_date'
]
=
settings
[
'start_date'
]
+
delta
return
tree
.
_replace
(
settings
=
settings
,
children
=
children
)
# class AppendModule(QueryPolicy):
# """
# This module will append a policy after each module matching the query.
# Any keys in policy_to_copy will be copied from the usage node that
# matches the query.
# """
# def __init__(self, query, source, policy_to_copy=None, *args, **kwargs):
# super(AppendModule, self).__init__(query, *args, **kwargs)
# self.policy_to_copy = policy_to_copy if policy_to_copy is not None else []
# self.source = source
# def update(usage):
# """
# Return a list of usages to replace the returned usage with
# """
# to_insert = Usage.create_usage(self.source)
# policy = dict(to_insert.policy)
# for key in self.policy_to_copy:
# if key in usage:
# policy[key] = usage[key]
# return [usage, to_insert._replace(policy=policy)]
class
CourseDescriptor
(
SequenceDescriptor
):
module_class
=
SequenceModule
...
...
common/lib/xmodule/xmodule/structure_module.py
0 → 100644
View file @
13efeaa5
from
collections
import
namedtuple
from
.xmodule
import
XModule
# N.B. it would be nice to make settings a frozen dictionary, and children a frozen list
# to force usages to behave entirely like values
class
Usage
(
namedtuple
(
'Usage'
,
'id source settings children'
)):
__slots__
=
()
@classmethod
def
create_usage
(
cls
,
source
):
xmodule
=
xmodule
.
get_module
(
source
)
return
Usage
(
uuid
(),
xmodule
.
id
,
xmodule
.
course_settings
,
[],
)
def
as_json
(
self
):
json
=
self
.
_asdict
()
json
[
'children'
]
=
[
child
.
as_json
()
for
child
in
json
[
'children'
]]
return
json
def
load_usage
(
usage_tree
):
"""
usage_tree is a nested set of dictionaries with the following keys:
id: the uuid of the usage
source: the id and version of the xmodule that this usage is an instance of
settings: default settings values set by the source xmodule
children: child usages
"""
usage_tree
[
'children'
]
=
[
load_usage
(
child
)
for
child
in
usage_tree
[
'children'
]]
return
Usage
(
**
usage_tree
)
class
StructureModule
(
XModule
):
def
__init__
(
self
,
*
args
,
**
kwargs
):
super
(
StructureModule
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
self
.
_usage_tree
=
None
@property
def
usage_tree
(
self
):
if
self
.
_usage_tree
is
None
:
self
.
_usage_tree
=
load_usage
(
self
.
content
[
'usage_tree'
])
return
self
.
_usage_tree
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