Commit a81015a9 by Calen Pennington

Move I4xSystem into xmodule so that the definition is available to the CMS

parent 97cdb67b
......@@ -80,7 +80,7 @@ class LoncapaProblem(object):
- id (string): identifier for this problem; often a filename (no spaces)
- state (dict): student state
- seed (int): random number generator seed (int)
- system (I4xSystem): I4xSystem instance which provides OS, rendering, and user context
- system (ModuleSystem): ModuleSystem instance which provides OS, rendering, and user context
'''
......@@ -286,7 +286,7 @@ class LoncapaProblem(object):
file = inc.get('file')
if file is not None:
try:
ifp = self.system.filestore.open(file) # open using I4xSystem OSFS filestore
ifp = self.system.filestore.open(file) # open using ModuleSystem OSFS filestore
except Exception as err:
log.error('Error %s in problem xml include: %s' % (err, etree.tostring(inc, pretty_print=True)))
log.error('Cannot find file %s in %s' % (file, self.system.filestore))
......
......@@ -50,7 +50,7 @@ class SimpleInput():# XModule
'''
Instantiate a SimpleInput class. Arguments:
- system : I4xSystem instance which provides OS, rendering, and user context
- system : ModuleSystem instance which provides OS, rendering, and user context
- xml : Element tree of this Input element
- item_id : id for this input element (assigned by capa_problem.LoncapProblem) - string
- track_url : URL used for tracking - string
......
......@@ -101,7 +101,7 @@ class LoncapaResponse(object):
- xml : ElementTree of this Response
- inputfields : ordered list of ElementTrees for each input entry field in this Response
- context : script processor context
- system : I4xSystem instance which provides OS, rendering, and user context
- system : ModuleSystem instance which provides OS, rendering, and user context
'''
self.xml = xml
......
......@@ -16,33 +16,23 @@ import capa.calc as calc
import capa.capa_problem as lcp
from capa.correctmap import CorrectMap
from xmodule import graders, x_module
from xmodule.x_module import ModuleSystem
from xmodule.graders import Score, aggregate_scores
from xmodule.progress import Progress
from nose.plugins.skip import SkipTest
class I4xSystem(object):
'''
This is an abstraction such that x_modules can function independent
of the courseware (e.g. import into other types of courseware, LMS,
or if we want to have a sandbox server for user-contributed content)
'''
def __init__(self):
self.ajax_url = '/'
self.track_function = lambda x: None
self.filestore = fs.osfs.OSFS(os.path.dirname(os.path.realpath(__file__)))
self.render_function = lambda x: {} # Probably incorrect
self.module_from_xml = lambda x: None # May need a real impl...
self.exception404 = Exception
self.DEBUG = True
def __repr__(self):
return repr(self.__dict__)
def __str__(self):
return str(self.__dict__)
i4xs = I4xSystem()
from mock import Mock
i4xs = ModuleSystem(
ajax_url='/',
track_function=Mock(),
get_module=Mock(),
render_template=Mock(),
replace_urls=Mock(),
user=Mock(),
filestore=fs.osfs.OSFS(os.path.dirname(os.path.realpath(__file__))),
debug=True,
xqueue_callback_url='/'
)
class ModelsTest(unittest.TestCase):
......
......@@ -5,14 +5,13 @@ import json
import logging
import traceback
import re
import StringIO
import os
from datetime import timedelta
from lxml import etree
from xmodule.x_module import XModule
from xmodule.raw_module import RawDescriptor
from xmodule.exceptions import NotFoundError
from progress import Progress
from capa.capa_problem import LoncapaProblem
from capa.responsetypes import StudentInputError
......@@ -319,8 +318,8 @@ class CapaModule(XModule):
if self.show_answer == 'always':
return True
#TODO: Not 404
raise self.system.exception404
return False
def update_score(self, get):
"""
......@@ -347,7 +346,7 @@ class CapaModule(XModule):
Returns the answers: {'answers' : answers}
'''
if not self.answer_available():
raise self.system.exception404
raise NotFoundError('Answer is not available')
else:
answers = self.lcp.get_question_answers()
return {'answers': answers}
......@@ -403,15 +402,14 @@ class CapaModule(XModule):
if self.closed():
event_info['failure'] = 'closed'
self.system.track_function('save_problem_check_fail', event_info)
# TODO (vshnayder): probably not 404?
raise self.system.exception404
raise NotFoundError('Problem is closed')
# Problem submitted. Student should reset before checking
# again.
if self.lcp.done and self.rerandomize == "always":
event_info['failure'] = 'unreset'
self.system.track_function('save_problem_check_fail', event_info)
raise self.system.exception404
raise NotFoundError('Problem must be reset before it can be checked again')
try:
old_state = self.lcp.get_state()
......
......@@ -4,7 +4,6 @@ import logging
from xmodule.modulestore import Location
from xmodule.seq_module import SequenceDescriptor, SequenceModule
from fs.errors import ResourceNotFoundError
log = logging.getLogger(__name__)
......
class InvalidDefinitionError(Exception):
pass
class NotFoundError(Exception):
pass
......@@ -7,6 +7,7 @@ from xmodule.mako_module import MakoModuleDescriptor
from xmodule.xml_module import XmlDescriptor
from xmodule.x_module import XModule
from xmodule.progress import Progress
from xmodule.exceptions import NotFoundError
log = logging.getLogger("mitx.common.lib.seq_module")
......@@ -56,7 +57,7 @@ class SequenceModule(XModule):
if dispatch == 'goto_position':
self.position = int(get['position'])
return json.dumps({'success': True})
raise self.system.exception404
raise NotFoundError('Unexpected dispatch type')
def render(self):
if self.rendered:
......
......@@ -81,7 +81,7 @@ class XModule(object):
'''
Construct a new xmodule
system: An I4xSystem allowing access to external resources
system: A ModuleSystem allowing access to external resources
location: Something Location-like that identifies this xmodule
definition: A dictionary containing 'data' and 'children'. Both are optional
'data': is JSON-like (string, dictionary, list, bool, or None, optionally nested).
......@@ -452,3 +452,62 @@ class XMLParsingSystem(DescriptorSystem):
"""
DescriptorSystem.__init__(self, load_item, resources_fs)
self.process_xml = process_xml
class ModuleSystem(object):
'''
This is an abstraction such that x_modules can function independent
of the courseware (e.g. import into other types of courseware, LMS,
or if we want to have a sandbox server for user-contributed content)
ModuleSystem objects are passed to x_modules to provide access to system
functionality.
Note that these functions can be closures over e.g. a django request
and user, or other environment-specific info.
'''
def __init__(self, ajax_url, track_function,
get_module, render_template, replace_urls,
user=None, filestore=None, debug=False, xqueue_callback_url=None):
'''
Create a closure around the system environment.
ajax_url - the url where ajax calls to the encapsulating module go.
track_function - function of (event_type, event), intended for logging
or otherwise tracking the event.
TODO: Not used, and has inconsistent args in different
files. Update or remove.
get_module - function that takes (location) and returns a corresponding
module instance object.
render_template - a function that takes (template_file, context), and returns
rendered html.
user - The user to base the random number generator seed off of for this request
filestore - A filestore ojbect. Defaults to an instance of OSFS based at
settings.DATA_DIR.
replace_urls - TEMPORARY - A function like static_replace.replace_urls
that capa_module can use to fix up the static urls in ajax results.
'''
self.ajax_url = ajax_url
self.xqueue_callback_url = xqueue_callback_url
self.track_function = track_function
self.filestore = filestore
self.get_module = get_module
self.render_template = render_template
self.DEBUG = self.debug = debug
self.seed = user.id if user is not None else 0
self.replace_urls = replace_urls
def get(self, attr):
''' provide uniform access to attributes (like etree).'''
return self.__dict__.get(attr)
def set(self, attr, val):
'''provide uniform access to attributes (like etree)'''
self.__dict__[attr] = val
def __repr__(self):
return repr(self.__dict__)
def __str__(self):
return str(self.__dict__)
......@@ -12,75 +12,16 @@ from xmodule.modulestore.django import modulestore
from mitxmako.shortcuts import render_to_string
from models import StudentModule, StudentModuleCache
from static_replace import replace_urls
from xmodule.exceptions import NotFoundError
from xmodule.x_module import ModuleSystem
log = logging.getLogger("mitx.courseware")
class I4xSystem(object):
'''
This is an abstraction such that x_modules can function independent
of the courseware (e.g. import into other types of courseware, LMS,
or if we want to have a sandbox server for user-contributed content)
I4xSystem objects are passed to x_modules to provide access to system
functionality.
Note that these functions can be closures over e.g. a django request
and user, or other environment-specific info.
'''
def __init__(self, ajax_url, track_function,
get_module, render_template, replace_urls,
user=None, filestore=None, xqueue_callback_url=None):
'''
Create a closure around the system environment.
ajax_url - the url where ajax calls to the encapsulating module go.
xqueue_callback_url - the url where external queueing system (e.g. for grading)
returns its response
track_function - function of (event_type, event), intended for logging
or otherwise tracking the event.
TODO: Not used, and has inconsistent args in different
files. Update or remove.
get_module - function that takes (location) and returns a corresponding
module instance object.
render_template - a function that takes (template_file, context), and returns
rendered html.
user - The user to base the random number generator seed off of for this request
filestore - A filestore ojbect. Defaults to an instance of OSFS based at
settings.DATA_DIR.
replace_urls - TEMPORARY - A function like static_replace.replace_urls
that capa_module can use to fix up the static urls in ajax results.
'''
self.ajax_url = ajax_url
self.xqueue_callback_url = xqueue_callback_url
self.track_function = track_function
self.filestore = filestore
self.get_module = get_module
self.render_template = render_template
self.exception404 = Http404
self.DEBUG = settings.DEBUG
self.seed = user.id if user is not None else 0
self.replace_urls = replace_urls
def get(self, attr):
''' provide uniform access to attributes (like etree).'''
return self.__dict__.get(attr)
def set(self, attr, val):
'''provide uniform access to attributes (like etree)'''
self.__dict__[attr] = val
def __repr__(self):
return repr(self.__dict__)
def __str__(self):
return str(self.__dict__)
def make_track_function(request):
'''
Make a tracking function that logs what happened.
For use in I4xSystem.
For use in ModuleSystem.
'''
import track.views
......@@ -221,20 +162,20 @@ def get_module(user, request, location, student_module_cache, position=None):
# TODO (cpennington): When modules are shared between courses, the static
# prefix is going to have to be specific to the module, not the directory
# that the xml was loaded from
system = I4xSystem(track_function=make_track_function(request),
render_template=render_to_string,
ajax_url=ajax_url,
xqueue_callback_url=xqueue_callback_url,
# TODO (cpennington): Figure out how to share info between systems
filestore=descriptor.system.resources_fs,
get_module=_get_module,
user=user,
# TODO (cpennington): This should be removed when all html from
# a module is coming through get_html and is therefore covered
# by the replace_static_urls code below
replace_urls=replace_urls,
)
# pass position specified in URL to module through I4xSystem
system = ModuleSystem(track_function=make_track_function(request),
render_template=render_to_string,
ajax_url=ajax_url,
xqueue_callback_url=xqueue_callback_url,
# TODO (cpennington): Figure out how to share info between systems
filestore=descriptor.system.resources_fs,
get_module=_get_module,
user=user,
# TODO (cpennington): This should be removed when all html from
# a module is coming through get_html and is therefore covered
# by the replace_static_urls code below
replace_urls=replace_urls,
)
# pass position specified in URL to module through ModuleSystem
system.set('position', position)
module = descriptor.xmodule_constructor(system)(instance_state, shared_state)
......@@ -407,6 +348,9 @@ def modx_dispatch(request, dispatch=None, id=None):
# Let the module handle the AJAX
try:
ajax_return = instance.handle_ajax(dispatch, request.POST)
except NotFoundError:
log.exception("Module indicating to user that request doesn't exist")
raise Http404
except:
log.exception("error processing ajax call")
raise
......
......@@ -25,7 +25,7 @@ import track.views
from lxml import etree
from courseware.module_render import make_track_function, I4xSystem, get_module
from courseware.module_render import make_track_function, ModuleSystem, get_module
from courseware.models import StudentModule
from multicourse import multicourse_settings
from student.models import UserProfile
......@@ -206,13 +206,12 @@ def quickedit(request, id=None, qetemplate='quickedit.html', coursename=None):
ajax_url = settings.MITX_ROOT_URL + '/modx/' + id + '/'
# Create the module (instance of capa_module.Module)
system = I4xSystem(track_function=make_track_function(request),
render_function=None,
render_template=render_to_string,
ajax_url=ajax_url,
filestore=OSFS(settings.DATA_DIR + xp),
#role = 'staff' if request.user.is_staff else 'student', # TODO: generalize this
)
system = ModuleSystem(track_function=make_track_function(request),
render_function=None,
render_template=render_to_string,
ajax_url=ajax_url,
filestore=OSFS(settings.DATA_DIR + xp),
)
instance = xmodule.get_module_class(module)(system,
xml,
id,
......
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