Add support metadata in policy.json

* if there is a policy.json in the course dir, read it
* file format is a dict with keys {category}/{url_name}, and values metadata dictionaries
* apply the policy, overwriting keys that are in the xml
* then do metadata inheritance, inheriting any overwritten keys.

* also a management cmd to generate a policy.json from a course dir.
This commit is contained in:
Victor Shnayder
2012-08-15 14:36:06 -04:00
parent a2057f9ea4
commit a4d67bab33
3 changed files with 152 additions and 2 deletions

View File

@@ -1,3 +1,4 @@
import json
import logging
import os
import re
@@ -149,7 +150,7 @@ class XMLModuleStore(ModuleStoreBase):
for course_dir in course_dirs:
self.try_load_course(course_dir)
def try_load_course(self,course_dir):
def try_load_course(self, course_dir):
'''
Load a course, keeping track of errors as we go along.
'''
@@ -170,7 +171,27 @@ class XMLModuleStore(ModuleStoreBase):
'''
String representation - for debugging
'''
return '<XMLModuleStore>data_dir=%s, %d courses, %d modules' % (self.data_dir,len(self.courses),len(self.modules))
return '<XMLModuleStore>data_dir=%s, %d courses, %d modules' % (
self.data_dir, len(self.courses), len(self.modules))
def load_policy(self, policy_path, tracker):
"""
Attempt to read a course policy from policy_path. If the file
exists, but is invalid, log an error and return {}.
If the policy loads correctly, returns the deserialized version.
"""
if not os.path.exists(policy_path):
return {}
try:
with open(policy_path) as f:
return json.load(f)
except (IOError, ValueError) as err:
msg = "Error loading course policy from {}".format(policy_path)
tracker(msg)
log.warning(msg + " " + str(err))
return {}
def load_course(self, course_dir, tracker):
"""
@@ -214,6 +235,11 @@ class XMLModuleStore(ModuleStoreBase):
system = ImportSystem(self, org, course, course_dir, tracker)
course_descriptor = system.process_xml(etree.tostring(course_data))
policy_path = self.data_dir / course_dir / 'policy.json'
policy = self.load_policy(policy_path, tracker)
XModuleDescriptor.apply_policy(course_descriptor, policy)
# NOTE: The descriptors end up loading somewhat bottom up, which
# breaks metadata inheritance via get_children(). Instead
# (actually, in addition to, for now), we do a final inheritance pass

View File

@@ -298,6 +298,14 @@ class XModule(HTMLSnippet):
return ""
def policy_key(location):
"""
Get the key for a location in a policy file. (Since the policy file is
specific to a course, it doesn't need the full location url).
"""
return '{cat}/{name}'.format(cat=location.category, name=location.name)
class XModuleDescriptor(Plugin, HTMLSnippet):
"""
An XModuleDescriptor is a specification for an element of a course. This
@@ -416,6 +424,24 @@ class XModuleDescriptor(Plugin, HTMLSnippet):
return dict((k,v) for k,v in self.metadata.items()
if k not in self._inherited_metadata)
@staticmethod
def apply_policy(node, policy):
"""
Given a descriptor, traverse all its descendants and update its metadata
with the policy.
Notes:
- this does not propagate inherited metadata. The caller should
call compute_inherited_metadata after applying the policy.
- metadata specified in the policy overrides metadata in the xml
"""
k = policy_key(node.location)
if k in policy:
node.metadata.update(policy[k])
for c in node.get_children():
XModuleDescriptor.apply_policy(c, policy)
@staticmethod
def compute_inherited_metadata(node):
"""Given a descriptor, traverse all of its descendants and do metadata