Commit 8febbf24 by Brittany Cheng

Merge branch 'master' of github.com:MITx/mitx into discussion2

parents efc0cc7c a7ae7905
...@@ -176,7 +176,7 @@ def load_preview_state(request, preview_id, location): ...@@ -176,7 +176,7 @@ def load_preview_state(request, preview_id, location):
def save_preview_state(request, preview_id, location, instance_state, shared_state): def save_preview_state(request, preview_id, location, instance_state, shared_state):
""" """
Load the state of a preview module to the request Save the state of a preview module to the request
preview_id (str): An identifier specifying which preview this module is used for preview_id (str): An identifier specifying which preview this module is used for
location: The Location of the module to dispatch to location: The Location of the module to dispatch to
......
...@@ -2,13 +2,18 @@ ...@@ -2,13 +2,18 @@
This config file runs the simplest dev environment""" This config file runs the simplest dev environment"""
from .common import * from .common import *
from .logsettings import get_logger_config
import logging import logging
import sys import sys
logging.basicConfig(stream=sys.stdout, )
DEBUG = True DEBUG = True
TEMPLATE_DEBUG = DEBUG TEMPLATE_DEBUG = DEBUG
LOGGING = get_logger_config(ENV_ROOT / "log",
logging_env="dev",
tracking_filename="tracking.log",
debug=True)
MODULESTORE = { MODULESTORE = {
'default': { 'default': {
......
...@@ -3,19 +3,19 @@ import os.path ...@@ -3,19 +3,19 @@ import os.path
import platform import platform
import sys import sys
def get_logger_config(log_dir, def get_logger_config(log_dir,
logging_env="no_env", logging_env="no_env",
tracking_filename=None, tracking_filename=None,
syslog_addr=None, syslog_addr=None,
debug=False): debug=False):
"""Return the appropriate logging config dictionary. You should assign the """Return the appropriate logging config dictionary. You should assign the
result of this to the LOGGING var in your settings. The reason it's done result of this to the LOGGING var in your settings. The reason it's done
this way instead of registering directly is because I didn't want to worry this way instead of registering directly is because I didn't want to worry
about resetting the logging state if this is called multiple times when about resetting the logging state if this is called multiple times when
settings are extended.""" settings are extended."""
# If we're given an explicit place to put tracking logs, we do that (say for # If we're given an explicit place to put tracking logs, we do that (say for
# debugging). However, logging is not safe for multiple processes hitting # debugging). However, logging is not safe for multiple processes hitting
# the same file. So if it's left blank, we dynamically create the filename # the same file. So if it's left blank, we dynamically create the filename
# based on the PID of this worker process. # based on the PID of this worker process.
if tracking_filename: if tracking_filename:
...@@ -33,6 +33,7 @@ def get_logger_config(log_dir, ...@@ -33,6 +33,7 @@ def get_logger_config(log_dir,
return { return {
'version': 1, 'version': 1,
'disable_existing_loggers': False,
'formatters' : { 'formatters' : {
'standard' : { 'standard' : {
'format' : '%(asctime)s %(levelname)s %(process)d [%(name)s] %(filename)s:%(lineno)d - %(message)s', 'format' : '%(asctime)s %(levelname)s %(process)d [%(name)s] %(filename)s:%(lineno)d - %(message)s',
......
from xmodule.modulestore.xml import XMLModuleStore from xmodule.modulestore.xml import XMLModuleStore
from nose.tools import assert_equals from nose.tools import assert_equals
from nose import SkipTest
from tempfile import mkdtemp from tempfile import mkdtemp
from fs.osfs import OSFS from fs.osfs import OSFS
...@@ -26,3 +27,10 @@ def check_export_roundtrip(data_dir): ...@@ -26,3 +27,10 @@ def check_export_roundtrip(data_dir):
for location in initial_import.modules.keys(): for location in initial_import.modules.keys():
print "Checking", location print "Checking", location
assert_equals(initial_import.modules[location], second_import.modules[location]) assert_equals(initial_import.modules[location], second_import.modules[location])
def test_toy_roundtrip():
dir = ""
# TODO: add paths and make this run.
raise SkipTest()
check_export_roundtrip(dir)
from path import path from path import path
import unittest import unittest
from fs.memoryfs import MemoryFS
from lxml import etree from lxml import etree
from xmodule.x_module import XMLParsingSystem, XModuleDescriptor from xmodule.x_module import XMLParsingSystem, XModuleDescriptor
from xmodule.errortracker import null_error_tracker from xmodule.errortracker import make_error_tracker
from xmodule.modulestore import Location from xmodule.modulestore import Location
from xmodule.modulestore.exceptions import ItemNotFoundError
class ImportTestCase(unittest.TestCase): ORG = 'test_org'
'''Make sure module imports work properly, including for malformed inputs''' COURSE = 'test_course'
class DummySystem(XMLParsingSystem):
def __init__(self):
self.modules = {}
self.resources_fs = MemoryFS()
self.errorlog = make_error_tracker()
@staticmethod
def get_system():
'''Get a dummy system'''
# Shouldn't need any system params, because the initial parse should fail
def load_item(loc): def load_item(loc):
raise Exception("Shouldn't be called") loc = Location(loc)
if loc in self.modules:
return self.modules[loc]
resources_fs = None print "modules: "
print self.modules
raise ItemNotFoundError("Can't find item at loc: {0}".format(loc))
def process_xml(xml): def process_xml(xml):
raise Exception("Shouldn't be called") print "loading {0}".format(xml)
descriptor = XModuleDescriptor.load_from_xml(xml, self, ORG, COURSE, None)
# Need to save module so we can find it later
self.modules[descriptor.location] = descriptor
# always eager
descriptor.get_children()
return descriptor
XMLParsingSystem.__init__(self, load_item, self.resources_fs,
self.errorlog.tracker, process_xml)
def render_template(template, context): def render_template(self, template, context):
raise Exception("Shouldn't be called") raise Exception("Shouldn't be called")
system = XMLParsingSystem(load_item, resources_fs,
null_error_tracker, process_xml)
system.render_template = render_template
return system
class ImportTestCase(unittest.TestCase):
'''Make sure module imports work properly, including for malformed inputs'''
@staticmethod
def get_system():
'''Get a dummy system'''
return DummySystem()
def test_fallback(self): def test_fallback(self):
'''Make sure that malformed xml loads as an ErrorDescriptor.''' '''Make sure that malformed xml loads as an ErrorDescriptor.'''
...@@ -85,3 +110,30 @@ class ImportTestCase(unittest.TestCase): ...@@ -85,3 +110,30 @@ class ImportTestCase(unittest.TestCase):
xml_out = etree.fromstring(xml_str_out) xml_out = etree.fromstring(xml_str_out)
self.assertEqual(xml_out.tag, 'sequential') self.assertEqual(xml_out.tag, 'sequential')
def test_metadata_inherit(self):
"""Make sure metadata inherits properly"""
system = self.get_system()
v = "1 hour"
start_xml = '''<course graceperiod="{grace}" url_name="test1" display_name="myseq">
<chapter url="hi" url_name="ch" display_name="CH">
<html url_name="h" display_name="H">Two houses, ...</html></chapter>
</course>'''.format(grace=v)
descriptor = XModuleDescriptor.load_from_xml(start_xml, system,
'org', 'course')
print "Errors: {0}".format(system.errorlog.errors)
print descriptor, descriptor.metadata
self.assertEqual(descriptor.metadata['graceperiod'], v)
# Check that the child inherits correctly
child = descriptor.get_children()[0]
self.assertEqual(child.metadata['graceperiod'], v)
# Now export and see if the chapter tag has a graceperiod attribute
resource_fs = MemoryFS()
exported_xml = descriptor.export_to_xml(resource_fs)
print "Exported xml:", exported_xml
root = etree.fromstring(exported_xml)
chapter_tag = root[0]
self.assertEqual(chapter_tag.tag, 'chapter')
self.assertFalse('graceperiod' in chapter_tag.attrib)
...@@ -41,6 +41,9 @@ class ImportSystem(XMLParsingSystem, MakoDescriptorSystem): ...@@ -41,6 +41,9 @@ class ImportSystem(XMLParsingSystem, MakoDescriptorSystem):
self.used_slugs = set() self.used_slugs = set()
def process_xml(xml): def process_xml(xml):
"""Takes an xml string, and returns a XModuleDescriptor created from
that xml.
"""
try: try:
# VS[compat] # VS[compat]
# TODO (cpennington): Remove this once all fall 2012 courses # TODO (cpennington): Remove this once all fall 2012 courses
......
...@@ -35,6 +35,8 @@ def import_from_xml(store, data_dir, course_dirs=None, eager=True, ...@@ -35,6 +35,8 @@ def import_from_xml(store, data_dir, course_dirs=None, eager=True,
store.update_item(module.location, module.definition['data']) store.update_item(module.location, module.definition['data'])
if 'children' in module.definition: if 'children' in module.definition:
store.update_children(module.location, module.definition['children']) store.update_children(module.location, module.definition['children'])
store.update_metadata(module.location, dict(module.metadata)) # NOTE: It's important to use own_metadata here to avoid writing
# inherited metadata everywhere.
store.update_metadata(module.location, dict(module.own_metadata))
return module_store return module_store
...@@ -358,6 +358,14 @@ class XModuleDescriptor(Plugin, HTMLSnippet): ...@@ -358,6 +358,14 @@ class XModuleDescriptor(Plugin, HTMLSnippet):
self._child_instances = None self._child_instances = None
self._inherited_metadata = set() self._inherited_metadata = set()
@property
def own_metadata(self):
"""
Return the metadata that is not inherited, but was defined on this module.
"""
return dict((k,v) for k,v in self.metadata.items()
if k not in self._inherited_metadata)
def inherit_metadata(self, metadata): def inherit_metadata(self, metadata):
""" """
Updates this module with metadata inherited from a containing module. Updates this module with metadata inherited from a containing module.
......
...@@ -53,14 +53,12 @@ def import_with_checks(course_dir, verbose=True): ...@@ -53,14 +53,12 @@ def import_with_checks(course_dir, verbose=True):
data_dir = course_dir.dirname() data_dir = course_dir.dirname()
course_dirs = [course_dir.basename()] course_dirs = [course_dir.basename()]
(error_tracker, errors) = make_error_tracker()
# No default class--want to complain if it doesn't find plugins for any # No default class--want to complain if it doesn't find plugins for any
# module. # module.
modulestore = XMLModuleStore(data_dir, modulestore = XMLModuleStore(data_dir,
default_class=None, default_class=None,
eager=True, eager=True,
course_dirs=course_dirs, course_dirs=course_dirs)
error_tracker=error_tracker)
def str_of_err(tpl): def str_of_err(tpl):
(msg, exc_info) = tpl (msg, exc_info) = tpl
...@@ -71,6 +69,15 @@ def import_with_checks(course_dir, verbose=True): ...@@ -71,6 +69,15 @@ def import_with_checks(course_dir, verbose=True):
return '{msg}\n{exc}'.format(msg=msg, exc=exc_str) return '{msg}\n{exc}'.format(msg=msg, exc=exc_str)
courses = modulestore.get_courses() courses = modulestore.get_courses()
n = len(courses)
if n != 1:
print 'ERROR: Expect exactly 1 course. Loaded {n}: {lst}'.format(
n=n, lst=courses)
return (False, None)
course = courses[0]
errors = modulestore.get_item_errors(course.location)
if len(errors) != 0: if len(errors) != 0:
all_ok = False all_ok = False
print '\n' print '\n'
...@@ -80,13 +87,6 @@ def import_with_checks(course_dir, verbose=True): ...@@ -80,13 +87,6 @@ def import_with_checks(course_dir, verbose=True):
print "=" * 40 print "=" * 40
print '\n' print '\n'
n = len(courses)
if n != 1:
print 'ERROR: Expect exactly 1 course. Loaded {n}: {lst}'.format(
n=n, lst=courses)
return (False, None)
course = courses[0]
#print course #print course
validators = ( validators = (
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment