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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user