Commit 8ba41635 by Calen Pennington

WIP. Data loads, but not all of it

parent 3d6cbf47
...@@ -499,7 +499,7 @@ def load_preview_module(request, preview_id, descriptor, instance_state, shared_ ...@@ -499,7 +499,7 @@ def load_preview_module(request, preview_id, descriptor, instance_state, shared_
""" """
system = preview_module_system(request, preview_id, descriptor) system = preview_module_system(request, preview_id, descriptor)
try: try:
module = descriptor.xmodule_constructor(system)(instance_state, shared_state) module = descriptor.xmodule(system)
except: except:
module = ErrorDescriptor.from_descriptor( module = ErrorDescriptor.from_descriptor(
descriptor, descriptor,
......
...@@ -83,7 +83,7 @@ class LoncapaProblem(object): ...@@ -83,7 +83,7 @@ class LoncapaProblem(object):
Main class for capa Problems. Main class for capa Problems.
''' '''
def __init__(self, problem_text, id, state=None, seed=None, system=None): def __init__(self, problem_text, id, correct_map=None, done=None, seed=None, system=None):
''' '''
Initializes capa Problem. Initializes capa Problem.
...@@ -91,7 +91,8 @@ class LoncapaProblem(object): ...@@ -91,7 +91,8 @@ class LoncapaProblem(object):
- problem_text (string): xml defining the problem - problem_text (string): xml defining the problem
- id (string): identifier for this problem; often a filename (no spaces) - id (string): identifier for this problem; often a filename (no spaces)
- state (dict): student state - correct_map (dict): data specifying whether the student has completed the problem
- done (bool): Whether the student has answered the problem
- seed (int): random number generator seed (int) - seed (int): random number generator seed (int)
- system (ModuleSystem): ModuleSystem instance which provides OS, - system (ModuleSystem): ModuleSystem instance which provides OS,
rendering, and user context rendering, and user context
...@@ -103,16 +104,11 @@ class LoncapaProblem(object): ...@@ -103,16 +104,11 @@ class LoncapaProblem(object):
self.problem_id = id self.problem_id = id
self.system = system self.system = system
self.seed = seed self.seed = seed
self.done = done
self.correct_map = CorrectMap()
if state: if correct_map is not None:
if 'seed' in state: self.correct_map.set_dict(correct_map)
self.seed = state['seed']
if 'student_answers' in state:
self.student_answers = state['student_answers']
if 'correct_map' in state:
self.correct_map.set_dict(state['correct_map'])
if 'done' in state:
self.done = state['done']
# TODO: Does this deplete the Linux entropy pool? Is this fast enough? # TODO: Does this deplete the Linux entropy pool? Is this fast enough?
if not self.seed: if not self.seed:
......
...@@ -7,6 +7,7 @@ from xmodule.x_module import XModule ...@@ -7,6 +7,7 @@ from xmodule.x_module import XModule
from xmodule.raw_module import RawDescriptor from xmodule.raw_module import RawDescriptor
from xmodule.xml_module import XmlDescriptor from xmodule.xml_module import XmlDescriptor
from xmodule.exceptions import InvalidDefinitionError from xmodule.exceptions import InvalidDefinitionError
from .model import String, Scope
DEFAULT = "_DEFAULT_GROUP" DEFAULT = "_DEFAULT_GROUP"
...@@ -68,37 +69,7 @@ class ABTestDescriptor(RawDescriptor, XmlDescriptor): ...@@ -68,37 +69,7 @@ class ABTestDescriptor(RawDescriptor, XmlDescriptor):
template_dir_name = "abtest" template_dir_name = "abtest"
def __init__(self, system, definition=None, **kwargs): experiment = String(help="Experiment that this A/B test belongs to", scope=Scope.content)
"""
definition is a dictionary with the following layout:
{'data': {
'experiment': 'the name of the experiment',
'group_portions': {
'group_a': 0.1,
'group_b': 0.2
},
'group_contents': {
'group_a': [
'url://for/content/module/1',
'url://for/content/module/2',
],
'group_b': [
'url://for/content/module/3',
],
DEFAULT: [
'url://for/default/content/1'
]
}
},
'children': [
'url://for/content/module/1',
'url://for/content/module/2',
'url://for/content/module/3',
'url://for/default/content/1',
]}
"""
kwargs['shared_state_key'] = definition['data']['experiment']
RawDescriptor.__init__(self, system, definition, **kwargs)
@classmethod @classmethod
def definition_from_xml(cls, xml_object, system): def definition_from_xml(cls, xml_object, system):
......
...@@ -12,6 +12,9 @@ import requests ...@@ -12,6 +12,9 @@ import requests
import time import time
import copy import copy
from .model import Scope, ModelType, List, String, Object, Boolean
Date = ModelType
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
...@@ -21,6 +24,39 @@ edx_xml_parser = etree.XMLParser(dtd_validation=False, load_dtd=False, ...@@ -21,6 +24,39 @@ edx_xml_parser = etree.XMLParser(dtd_validation=False, load_dtd=False,
class CourseDescriptor(SequenceDescriptor): class CourseDescriptor(SequenceDescriptor):
module_class = SequenceModule module_class = SequenceModule
textbooks = List(help="List of pairs of (title, url) for textbooks used in this course", default=[], scope=Scope.content)
wiki_slug = String(help="Slug that points to the wiki for this course", scope=Scope.content)
enrollment_start = Date(help="Date that enrollment for this class is opened", scope=Scope.settings)
enrollment_end = Date(help="Date that enrollment for this class is closed", scope=Scope.settings)
end = Date(help="Date that this class ends", scope=Scope.settings)
advertised_start = Date(help="Date that this course is advertised to start", scope=Scope.settings)
grading_policy = Object(help="Grading policy definition for this class", scope=Scope.content)
info_sidebar_name = String(scope=Scope.settings, default='Course Handouts')
# An extra property is used rather than the wiki_slug/number because
# there are courses that change the number for different runs. This allows
# courses to share the same css_class across runs even if they have
# different numbers.
#
# TODO get rid of this as soon as possible or potentially build in a robust
# way to add in course-specific styling. There needs to be a discussion
# about the right way to do this, but arjun will address this ASAP. Also
# note that the courseware template needs to change when this is removed.
css_class = String(help="DO NOT USE THIS", scope=Scope.settings)
# TODO: This is a quick kludge to allow CS50 (and other courses) to
# specify their own discussion forums as external links by specifying a
# "discussion_link" in their policy JSON file. This should later get
# folded in with Syllabus, Course Info, and additional Custom tabs in a
# more sensible framework later.
discussion_link = String(help="DO NOT USE THIS", scope=Scope.settings)
# TODO: same as above, intended to let internal CS50 hide the progress tab
# until we get grade integration set up.
# Explicit comparison to True because we always want to return a bool.
hide_progress_tab = Boolean(help="DO NOT USE THIS", scope=Scope.settings)
template_dir_name = 'course' template_dir_name = 'course'
class Textbook: class Textbook:
...@@ -69,10 +105,11 @@ class CourseDescriptor(SequenceDescriptor): ...@@ -69,10 +105,11 @@ class CourseDescriptor(SequenceDescriptor):
return table_of_contents return table_of_contents
def __init__(self, system, definition=None, **kwargs): def __init__(self, *args, **kwargs):
super(CourseDescriptor, self).__init__(system, definition, **kwargs) super(CourseDescriptor, self).__init__(*args, **kwargs)
self.textbooks = [] self.textbooks = []
for title, book_url in self.definition['data']['textbooks']: for title, book_url in self.textbooks:
try: try:
self.textbooks.append(self.Textbook(title, book_url)) self.textbooks.append(self.Textbook(title, book_url))
except: except:
...@@ -81,7 +118,8 @@ class CourseDescriptor(SequenceDescriptor): ...@@ -81,7 +118,8 @@ class CourseDescriptor(SequenceDescriptor):
log.exception("Couldn't load textbook ({0}, {1})".format(title, book_url)) log.exception("Couldn't load textbook ({0}, {1})".format(title, book_url))
continue continue
self.wiki_slug = self.definition['data']['wiki_slug'] or self.location.course if self.wiki_slug is None:
self.wiki_slug = self.location.course
msg = None msg = None
if self.start is None: if self.start is None:
...@@ -98,7 +136,7 @@ class CourseDescriptor(SequenceDescriptor): ...@@ -98,7 +136,7 @@ class CourseDescriptor(SequenceDescriptor):
# disable the syllabus content for courses that do not provide a syllabus # disable the syllabus content for courses that do not provide a syllabus
self.syllabus_present = self.system.resources_fs.exists(path('syllabus')) self.syllabus_present = self.system.resources_fs.exists(path('syllabus'))
self.set_grading_policy(self.definition['data'].get('grading_policy', None)) self.set_grading_policy(self.grading_policy)
def defaut_grading_policy(self): def defaut_grading_policy(self):
""" """
...@@ -203,7 +241,7 @@ class CourseDescriptor(SequenceDescriptor): ...@@ -203,7 +241,7 @@ class CourseDescriptor(SequenceDescriptor):
# cdodge: import the grading policy information that is on disk and put into the # cdodge: import the grading policy information that is on disk and put into the
# descriptor 'definition' bucket as a dictionary so that it is persisted in the DB # descriptor 'definition' bucket as a dictionary so that it is persisted in the DB
instance.definition['data']['grading_policy'] = policy instance.grading_policy = policy
# now set the current instance. set_grading_policy() will apply some inheritance rules # now set the current instance. set_grading_policy() will apply some inheritance rules
instance.set_grading_policy(policy) instance.set_grading_policy(policy)
...@@ -395,38 +433,14 @@ class CourseDescriptor(SequenceDescriptor): ...@@ -395,38 +433,14 @@ class CourseDescriptor(SequenceDescriptor):
@property @property
def start_date_text(self): def start_date_text(self):
displayed_start = self._try_parse_time('advertised_start') or self.start return time.strftime("%b %d, %Y", self.advertised_start or self.start)
return time.strftime("%b %d, %Y", displayed_start)
@property @property
def end_date_text(self): def end_date_text(self):
return time.strftime("%b %d, %Y", self.end) return time.strftime("%b %d, %Y", self.end)
# An extra property is used rather than the wiki_slug/number because
# there are courses that change the number for different runs. This allows
# courses to share the same css_class across runs even if they have
# different numbers.
#
# TODO get rid of this as soon as possible or potentially build in a robust
# way to add in course-specific styling. There needs to be a discussion
# about the right way to do this, but arjun will address this ASAP. Also
# note that the courseware template needs to change when this is removed.
@property
def css_class(self):
return self.metadata.get('css_class', '')
@property
def info_sidebar_name(self):
return self.metadata.get('info_sidebar_name', 'Course Handouts')
@property
def discussion_link(self):
"""TODO: This is a quick kludge to allow CS50 (and other courses) to
specify their own discussion forums as external links by specifying a
"discussion_link" in their policy JSON file. This should later get
folded in with Syllabus, Course Info, and additional Custom tabs in a
more sensible framework later."""
return self.metadata.get('discussion_link', None)
@property @property
def forum_posts_allowed(self): def forum_posts_allowed(self):
...@@ -443,12 +457,6 @@ class CourseDescriptor(SequenceDescriptor): ...@@ -443,12 +457,6 @@ class CourseDescriptor(SequenceDescriptor):
return True return True
@property
def hide_progress_tab(self):
"""TODO: same as above, intended to let internal CS50 hide the progress tab
until we get grade integration set up."""
# Explicit comparison to True because we always want to return a bool.
return self.metadata.get('hide_progress_tab') == True
@property @property
def end_of_course_survey_url(self): def end_of_course_survey_url(self):
......
...@@ -74,12 +74,11 @@ class ErrorDescriptor(JSONEditingDescriptor): ...@@ -74,12 +74,11 @@ class ErrorDescriptor(JSONEditingDescriptor):
} }
# real metadata stays in the content, but add a display name # real metadata stays in the content, but add a display name
metadata = {'display_name': 'Error: ' + location.name} model_data = {'display_name': 'Error: ' + location.name}
return ErrorDescriptor( return ErrorDescriptor(
system, system,
definition, location,
location=location, model_data,
metadata=metadata
) )
def get_context(self): def get_context(self):
......
...@@ -21,20 +21,19 @@ class MakoModuleDescriptor(XModuleDescriptor): ...@@ -21,20 +21,19 @@ class MakoModuleDescriptor(XModuleDescriptor):
the descriptor as the `module` parameter to that template the descriptor as the `module` parameter to that template
""" """
def __init__(self, system, definition=None, **kwargs): def __init__(self, system, location, model_data):
if getattr(system, 'render_template', None) is None: if getattr(system, 'render_template', None) is None:
raise TypeError('{system} must have a render_template function' raise TypeError('{system} must have a render_template function'
' in order to use a MakoDescriptor'.format( ' in order to use a MakoDescriptor'.format(
system=system)) system=system))
super(MakoModuleDescriptor, self).__init__(system, definition, **kwargs) super(MakoModuleDescriptor, self).__init__(system, location, model_data)
def get_context(self): def get_context(self):
""" """
Return the context to render the mako template with Return the context to render the mako template with
""" """
return {'module': self, return {'module': self,
'metadata': self.metadata, 'editable_metadata_fields': self.editable_fields
'editable_metadata_fields' : self.editable_metadata_fields
} }
def get_html(self): def get_html(self):
...@@ -44,6 +43,6 @@ class MakoModuleDescriptor(XModuleDescriptor): ...@@ -44,6 +43,6 @@ class MakoModuleDescriptor(XModuleDescriptor):
# cdodge: encapsulate a means to expose "editable" metadata fields (i.e. not internal system metadata) # cdodge: encapsulate a means to expose "editable" metadata fields (i.e. not internal system metadata)
@property @property
def editable_metadata_fields(self): def editable_metadata_fields(self):
subset = [name for name in self.metadata.keys() if name not in self.system_metadata_fields] subset = [field.name for field in self.fields if field.name not in self.system_metadata_fields]
return subset return subset
from collections import namedtuple
class ModuleScope(object):
USAGE, DEFINITION, TYPE, ALL = xrange(4)
class Scope(namedtuple('ScopeBase', 'student module')):
pass
Scope.content = Scope(student=False, module=ModuleScope.DEFINITION)
Scope.student_state = Scope(student=True, module=ModuleScope.USAGE)
Scope.settings = Scope(student=True, module=ModuleScope.USAGE)
Scope.student_preferences = Scope(student=True, module=ModuleScope.TYPE)
Scope.student_info = Scope(student=True, module=ModuleScope.ALL)
class ModelType(object):
sequence = 0
def __init__(self, help=None, default=None, scope=Scope.content):
self._seq = self.sequence
self._name = "unknown"
self.help = help
self.default = default
self.scope = scope
ModelType.sequence += 1
@property
def name(self):
return self._name
def __get__(self, instance, owner):
if instance is None:
return self
return instance._model_data.get(self.name, self.default)
def __set__(self, instance, value):
instance._model_data[self.name] = value
def __delete__(self, instance):
del instance._model_data[self.name]
def __repr__(self):
return "<{0.__class__.__name} {0.__name__}>".format(self)
def __lt__(self, other):
return self._seq < other._seq
Int = Float = Boolean = Object = List = String = Any = ModelType
class ModelMetaclass(type):
def __new__(cls, name, bases, attrs):
# Find registered methods
reg_methods = {}
for value in attrs.itervalues():
for reg_type, names in getattr(value, "_method_registrations", {}).iteritems():
for n in names:
reg_methods[reg_type + n] = value
attrs['registered_methods'] = reg_methods
if attrs.get('has_children', False):
attrs['children'] = ModelType(help='The children of this XModule', default=[], scope=None)
@property
def child_map(self):
return dict((child.name, child) for child in self.children)
attrs['child_map'] = child_map
fields = []
for n, v in attrs.items():
if isinstance(v, ModelType):
v._name = n
fields.append(v)
fields.sort()
attrs['fields'] = fields
return super(ModelMetaclass, cls).__new__(cls, name, bases, attrs)
...@@ -187,11 +187,12 @@ class ImportSystem(XMLParsingSystem, MakoDescriptorSystem): ...@@ -187,11 +187,12 @@ class ImportSystem(XMLParsingSystem, MakoDescriptorSystem):
err_msg err_msg
) )
descriptor.metadata['data_dir'] = course_dir setattr(descriptor, 'data_dir', course_dir)
xmlstore.modules[course_id][descriptor.location] = descriptor xmlstore.modules[course_id][descriptor.location] = descriptor
for child in descriptor.get_children(): if hasattr(descriptor, 'children'):
for child in descriptor.children:
parent_tracker.add_parent(child.location, descriptor.location) parent_tracker.add_parent(child.location, descriptor.location)
return descriptor return descriptor
...@@ -425,7 +426,7 @@ class XMLModuleStore(ModuleStoreBase): ...@@ -425,7 +426,7 @@ class XMLModuleStore(ModuleStoreBase):
# breaks metadata inheritance via get_children(). Instead # breaks metadata inheritance via get_children(). Instead
# (actually, in addition to, for now), we do a final inheritance pass # (actually, in addition to, for now), we do a final inheritance pass
# after we have the course descriptor. # after we have the course descriptor.
XModuleDescriptor.compute_inherited_metadata(course_descriptor) #XModuleDescriptor.compute_inherited_metadata(course_descriptor)
# now import all pieces of course_info which is expected to be stored # now import all pieces of course_info which is expected to be stored
# in <content_dir>/info or <content_dir>/info/<url_name> # in <content_dir>/info or <content_dir>/info/<url_name>
...@@ -449,22 +450,22 @@ class XMLModuleStore(ModuleStoreBase): ...@@ -449,22 +450,22 @@ class XMLModuleStore(ModuleStoreBase):
if not os.path.exists(path): if not os.path.exists(path):
path = base_dir path = base_dir
for filepath in glob.glob(path/ '*'): for filepath in glob.glob(path / '*'):
with open(filepath) as f: with open(filepath) as f:
try: try:
html = f.read().decode('utf-8') html = f.read().decode('utf-8')
# tabs are referenced in policy.json through a 'slug' which is just the filename without the .html suffix # tabs are referenced in policy.json through a 'slug' which is just the filename without the .html suffix
slug = os.path.splitext(os.path.basename(filepath))[0] slug = os.path.splitext(os.path.basename(filepath))[0]
loc = Location('i4x', course_descriptor.location.org, course_descriptor.location.course, category, slug) loc = Location('i4x', course_descriptor.location.org, course_descriptor.location.course, category, slug)
module = HtmlDescriptor(system, definition={'data' : html}, **{'location' : loc}) module = HtmlDescriptor(system, loc, {'data': html})
# VS[compat]: # VS[compat]:
# Hack because we need to pull in the 'display_name' for static tabs (because we need to edit them) # Hack because we need to pull in the 'display_name' for static tabs (because we need to edit them)
# from the course policy # from the course policy
if category == "static_tab": if category == "static_tab":
for tab in course_descriptor.tabs or []: for tab in course_descriptor.tabs or []:
if tab.get('url_slug') == slug: if tab.get('url_slug') == slug:
module.metadata['display_name'] = tab['name'] module.display_name = tab['name']
module.metadata['data_dir'] = course_dir module.data_dir = course_dir
self.modules[course_descriptor.id][module.location] = module self.modules[course_descriptor.id][module.location] = module
except Exception, e: except Exception, e:
logging.exception("Failed to load {0}. Skipping... Exception: {1}".format(filepath, str(e))) logging.exception("Failed to load {0}. Skipping... Exception: {1}".format(filepath, str(e)))
......
...@@ -67,10 +67,6 @@ class CustomTagDescriptor(RawDescriptor): ...@@ -67,10 +67,6 @@ class CustomTagDescriptor(RawDescriptor):
return template.render(**params) return template.render(**params)
def __init__(self, system, definition, **kwargs):
'''Render and save the template for this descriptor instance'''
super(CustomTagDescriptor, self).__init__(system, definition, **kwargs)
@property @property
def rendered_html(self): def rendered_html(self):
return self.render_template(self.system, self.definition['data']) return self.render_template(self.system, self.definition['data'])
......
...@@ -299,13 +299,13 @@ class XmlDescriptor(XModuleDescriptor): ...@@ -299,13 +299,13 @@ class XmlDescriptor(XModuleDescriptor):
metadata = cls.load_metadata(definition_xml) metadata = cls.load_metadata(definition_xml)
# move definition metadata into dict # move definition metadata into dict
dmdata = definition.get('definition_metadata','') dmdata = definition.get('definition_metadata', '')
if dmdata: if dmdata:
metadata['definition_metadata_raw'] = dmdata metadata['definition_metadata_raw'] = dmdata
try: try:
metadata.update(json.loads(dmdata)) metadata.update(json.loads(dmdata))
except Exception as err: except Exception as err:
log.debug('Error %s in loading metadata %s' % (err,dmdata)) log.debug('Error %s in loading metadata %s' % (err, dmdata))
metadata['definition_metadata_err'] = str(err) metadata['definition_metadata_err'] = str(err)
# Set/override any metadata specified by policy # Set/override any metadata specified by policy
...@@ -313,11 +313,14 @@ class XmlDescriptor(XModuleDescriptor): ...@@ -313,11 +313,14 @@ class XmlDescriptor(XModuleDescriptor):
if k in system.policy: if k in system.policy:
cls.apply_policy(metadata, system.policy[k]) cls.apply_policy(metadata, system.policy[k])
model_data = {}
model_data.update(metadata)
model_data.update(definition)
return cls( return cls(
system, system,
definition, location,
location=location, model_data,
metadata=metadata,
) )
@classmethod @classmethod
......
function github_status {
gcli status create mitx mitx $GIT_COMMIT \
--params=$1 \
target_url:$BUILD_URL \
description:"Build #$BUILD_NUMBER is running" \
-f csv
}
function github_mark_failed_on_exit {
trap '[ $? == "0" ] || github_status state:failed' EXIT
}
\ No newline at end of file
...@@ -85,7 +85,7 @@ def course_image_url(course): ...@@ -85,7 +85,7 @@ def course_image_url(course):
"""Try to look up the image url for the course. If it's not found, """Try to look up the image url for the course. If it's not found,
log an error and return the dead link""" log an error and return the dead link"""
if isinstance(modulestore(), XMLModuleStore): if isinstance(modulestore(), XMLModuleStore):
path = course.metadata['data_dir'] + "/images/course_image.jpg" path = course.data_dir + "/images/course_image.jpg"
return try_staticfiles_lookup(path) return try_staticfiles_lookup(path)
else: else:
loc = course.location._replace(tag='c4x', category='asset', name='images_course_image.jpg') loc = course.location._replace(tag='c4x', category='asset', name='images_course_image.jpg')
...@@ -162,7 +162,9 @@ def get_course_about_section(course, section_key): ...@@ -162,7 +162,9 @@ def get_course_about_section(course, section_key):
key=section_key, url=course.location.url())) key=section_key, url=course.location.url()))
return None return None
elif section_key == "title": elif section_key == "title":
return course.metadata.get('display_name', course.url_name) if course.display_name is None:
return course.url_name
return course.display_name
elif section_key == "university": elif section_key == "university":
return course.location.org return course.location.org
elif section_key == "number": elif section_key == "number":
...@@ -220,7 +222,7 @@ def get_course_syllabus_section(course, section_key): ...@@ -220,7 +222,7 @@ def get_course_syllabus_section(course, section_key):
filepath = find_file(fs, dirs, section_key + ".html") filepath = find_file(fs, dirs, section_key + ".html")
with fs.open(filepath) as htmlFile: with fs.open(filepath) as htmlFile:
return replace_urls(htmlFile.read().decode('utf-8'), return replace_urls(htmlFile.read().decode('utf-8'),
course.metadata['data_dir'], course_namespace=course.location) course.data_dir, course_namespace=course.location)
except ResourceNotFoundError: except ResourceNotFoundError:
log.exception("Missing syllabus section {key} in course {url}".format( log.exception("Missing syllabus section {key} in course {url}".format(
key=section_key, url=course.location.url())) key=section_key, url=course.location.url()))
......
...@@ -245,7 +245,7 @@ def _get_module(user, request, location, student_module_cache, course_id, positi ...@@ -245,7 +245,7 @@ def _get_module(user, request, location, student_module_cache, course_id, positi
make_psychometrics_data_update_handler(instance_module)) make_psychometrics_data_update_handler(instance_module))
try: try:
module = descriptor.xmodule_constructor(system)(instance_state, shared_state) module = descriptor.xmodule(system)
except: except:
log.exception("Error creating module from descriptor {0}".format(descriptor)) log.exception("Error creating module from descriptor {0}".format(descriptor))
...@@ -259,7 +259,7 @@ def _get_module(user, request, location, student_module_cache, course_id, positi ...@@ -259,7 +259,7 @@ def _get_module(user, request, location, student_module_cache, course_id, positi
error_msg=exc_info_to_str(sys.exc_info())) error_msg=exc_info_to_str(sys.exc_info()))
# Make an error module # Make an error module
return err_descriptor.xmodule_constructor(system)(None, None) return err_descriptor.xmodule(system)
_get_html = module.get_html _get_html = module.get_html
......
...@@ -40,7 +40,7 @@ end ...@@ -40,7 +40,7 @@ end
def django_admin(system, env, command, *args) def django_admin(system, env, command, *args)
django_admin = ENV['DJANGO_ADMIN_PATH'] || select_executable('django-admin.py', 'django-admin') django_admin = ENV['DJANGO_ADMIN_PATH'] || select_executable('django-admin.py', 'django-admin')
return "#{django_admin} #{command} --settings=#{system}.envs.#{env} --pythonpath=. #{args.join(' ')}" return "#{django_admin} #{command} --traceback --settings=#{system}.envs.#{env} --pythonpath=. #{args.join(' ')}"
end end
def django_for_jasmine(system, django_reload) def django_for_jasmine(system, django_reload)
......
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