diff --git a/djangoapps/courseware/capa/capa_problem.py b/djangoapps/courseware/capa/capa_problem.py index c5a81e8100..9835b598b2 100644 --- a/djangoapps/courseware/capa/capa_problem.py +++ b/djangoapps/courseware/capa/capa_problem.py @@ -53,13 +53,12 @@ html_special_response = {"textline":textline.render, "schematic":schematic.render} class LoncapaProblem(object): - def __init__(self, filename, id=None, state=None, seed=None): + def __init__(self, fileobject, id=None, state=None, seed=None): ## Initialize class variables from state self.seed = None self.student_answers = dict() self.correct_map = dict() self.done = False - self.filename = filename if seed != None: self.seed = seed @@ -69,7 +68,6 @@ class LoncapaProblem(object): else: print "NO ID" raise Exception("This should never happen (183)") - #self.problem_id = filename if state: if 'seed' in state: @@ -81,17 +79,12 @@ class LoncapaProblem(object): if 'done' in state: self.done = state['done'] -# print self.seed - # TODO: Does this deplete the Linux entropy pool? Is this fast enough? if not self.seed: self.seed=struct.unpack('i', os.urandom(4))[0] -# print filename, self.seed, seed - ## Parse XML file - #log.debug(u"LoncapaProblem() opening file {0}".format(filename)) - file_text = open(filename).read() + file_text = fileobject.read() # Convert startouttext and endouttext to proper # TODO: Do with XML operations file_text = re.sub("startouttext\s*/","text",file_text) diff --git a/djangoapps/courseware/module_render.py b/djangoapps/courseware/module_render.py index 2924aec9df..ac76eb08ae 100644 --- a/djangoapps/courseware/module_render.py +++ b/djangoapps/courseware/module_render.py @@ -18,8 +18,12 @@ from django.http import HttpResponse from django.shortcuts import redirect from django.template import Context from django.template import Context, loader + +from fs.osfs import OSFS + from mitxmako.shortcuts import render_to_response, render_to_string + from models import StudentModule from student.models import UserProfile import track.views @@ -30,6 +34,14 @@ import courseware.modules log = logging.getLogger("mitx.courseware") +class I4xSystem(object): + def __init__(self, ajax_url, track_function, render_function, filestore=None): + self.ajax_url = ajax_url + self.track_function = track_function + self.filestore = OSFS(settings.DATA_DIR) + self.render_function = render_function + self.exception404 = Http404 + def object_cache(cache, user, module_type, module_id): # We don't look up on user -- all queries include user # Additional lookup would require a DB hit the way Django @@ -76,12 +88,15 @@ def modx_dispatch(request, module=None, dispatch=None, id=None): xml = content_parser.module_xml(request.user, module, 'id', id) # Create the module - instance=courseware.modules.get_module_class(module)(xml, + system = I4xSystem(track_function = make_track_function(request), + render_function = None, + ajax_url = ajax_url, + filestore = None + ) + instance=courseware.modules.get_module_class(module)(system, + xml, id, - ajax_url=ajax_url, - state=oldstate, - track_function = make_track_function(request), - render_function = None) + state=oldstate) # Let the module handle the AJAX ajax_return=instance.handle_ajax(dispatch, request.POST) # Save the state back to the database @@ -128,12 +143,15 @@ def render_x_module(user, request, xml_module, module_object_preload): # Create a new instance ajax_url = '/modx/'+module_type+'/'+module_id+'/' - instance=module_class(etree.tostring(xml_module), + system = I4xSystem(track_function = make_track_function(request), + render_function = lambda x: render_module(user, request, x, module_object_preload), + ajax_url = ajax_url, + filestore = None + ) + instance=module_class(system, + etree.tostring(xml_module), module_id, - ajax_url=ajax_url, - state=state, - track_function = make_track_function(request), - render_function = lambda x: render_module(user, request, x, module_object_preload)) + state=state) # If instance wasn't already in the database, create it if not smod: diff --git a/djangoapps/courseware/modules/capa_module.py b/djangoapps/courseware/modules/capa_module.py index 86958dcf1c..5c2f6be5c3 100644 --- a/djangoapps/courseware/modules/capa_module.py +++ b/djangoapps/courseware/modules/capa_module.py @@ -16,9 +16,7 @@ import traceback from lxml import etree ## TODO: Abstract out from Django -from django.conf import settings -from mitxmako.shortcuts import render_to_response, render_to_string -from django.http import Http404 +from mitxmako.shortcuts import render_to_string from x_module import XModule from courseware.capa.capa_problem import LoncapaProblem, StudentInputError @@ -92,7 +90,6 @@ class Module(XModule): # User submitted a problem, and hasn't reset. We don't want # more submissions. if self.lcp.done and self.rerandomize == "always": - #print "!" check_button = False save_button = False @@ -131,8 +128,8 @@ class Module(XModule): return html - def __init__(self, xml, item_id, ajax_url=None, track_url=None, state=None, track_function=None, render_function = None, meta = None): - XModule.__init__(self, xml, item_id, ajax_url, track_url, state, track_function, render_function) + def __init__(self, system, xml, item_id, state=None): + XModule.__init__(self, system, xml, item_id, state) self.attempts = 0 self.max_attempts = None @@ -185,15 +182,14 @@ class Module(XModule): if state!=None and 'attempts' in state: self.attempts=state['attempts'] - self.filename=content_parser.item(dom2.xpath('/problem/@filename')) - filename=settings.DATA_DIR+"/problems/"+self.filename+".xml" + self.filename="problems/"+content_parser.item(dom2.xpath('/problem/@filename'))+".xml" self.name=content_parser.item(dom2.xpath('/problem/@name')) self.weight=content_parser.item(dom2.xpath('/problem/@weight')) if self.rerandomize == 'never': seed = 1 else: seed = None - self.lcp=LoncapaProblem(filename, self.item_id, state, seed = seed) + self.lcp=LoncapaProblem(self.filestore.open(self.filename), self.item_id, state, seed = seed) def handle_ajax(self, dispatch, get): if dispatch=='problem_get': @@ -242,16 +238,15 @@ class Module(XModule): if self.show_answer == 'closed' and not self.closed(): return False print "aa", self.show_answer - raise Http404 + raise self.system.exception404 #TODO: Not 404 def get_answer(self, get): if not self.answer_available(): - raise Http404 + raise self.system.exception404 else: return json.dumps(self.lcp.get_question_answers(), cls=ComplexEncoder) - # Figure out if we should move these to capa_problem? def get_problem(self, get): ''' Same as get_problem_html -- if we want to reconfirm we @@ -270,41 +265,33 @@ class Module(XModule): for key in get: answers['_'.join(key.split('_')[1:])]=get[key] -# print "XXX", answers, get - event_info['answers']=answers # Too late. Cannot submit if self.closed(): event_info['failure']='closed' self.tracker('save_problem_check_fail', event_info) - print "cp" - raise Http404 + raise self.system.exception404 # Problem submitted. Student should reset before checking # again. if self.lcp.done and self.rerandomize == "always": event_info['failure']='unreset' self.tracker('save_problem_check_fail', event_info) - print "cpdr" - raise Http404 + raise self.system.exception404 try: old_state = self.lcp.get_state() lcp_id = self.lcp.problem_id - filename = self.lcp.filename correct_map = self.lcp.grade_answers(answers) except StudentInputError as inst: - self.lcp = LoncapaProblem(filename, id=lcp_id, state=old_state) + self.lcp = LoncapaProblem(self.filestore.open(self.filename), id=lcp_id, state=old_state) traceback.print_exc() -# print {'error':sys.exc_info(), -# 'answers':answers, -# 'seed':self.lcp.seed, -# 'filename':self.lcp.filename} return json.dumps({'success':inst.message}) except: - self.lcp = LoncapaProblem(filename, id=lcp_id, state=old_state) + self.lcp = LoncapaProblem(self.filestore.open(self.filename), id=lcp_id, state=old_state) traceback.print_exc() + raise return json.dumps({'success':'Unknown Error'}) @@ -382,8 +369,8 @@ class Module(XModule): self.lcp.questions=dict() # Detailed info about questions in problem instance. TODO: Should be by id and not lid. self.lcp.seed=None - filename=settings.DATA_DIR+"problems/"+self.filename+".xml" - self.lcp=LoncapaProblem(filename, self.item_id, self.lcp.get_state()) + filename="problems/"+self.filename+".xml" + self.lcp=LoncapaProblem(self.filestore.open(filename), self.item_id, self.lcp.get_state()) event_info['new_state']=self.lcp.get_state() self.tracker('reset_problem', event_info) diff --git a/djangoapps/courseware/modules/html_module.py b/djangoapps/courseware/modules/html_module.py index 4194f73e74..77bcbb4bbc 100644 --- a/djangoapps/courseware/modules/html_module.py +++ b/djangoapps/courseware/modules/html_module.py @@ -1,7 +1,5 @@ import json -## TODO: Abstract out from Django -from django.conf import settings from mitxmako.shortcuts import render_to_response, render_to_string from x_module import XModule @@ -24,13 +22,13 @@ class Module(XModule): textlist=[i for i in textlist if type(i)==str] return "".join(textlist) try: - filename=settings.DATA_DIR+"html/"+self.filename - return open(filename).read() + filename="html/"+self.filename + return self.filestore.open(filename).read() except: # For backwards compatibility. TODO: Remove return render_to_string(self.filename, {'id': self.item_id}) - def __init__(self, xml, item_id, ajax_url=None, track_url=None, state=None, track_function=None, render_function = None): - XModule.__init__(self, xml, item_id, ajax_url, track_url, state, track_function, render_function) + def __init__(self, system, xml, item_id, state=None): + XModule.__init__(self, system, xml, item_id, state) xmltree=etree.fromstring(xml) self.filename = None filename_l=xmltree.xpath("/html/@filename") diff --git a/djangoapps/courseware/modules/schematic_module.py b/djangoapps/courseware/modules/schematic_module.py index e253f1acc6..5fef265e01 100644 --- a/djangoapps/courseware/modules/schematic_module.py +++ b/djangoapps/courseware/modules/schematic_module.py @@ -19,6 +19,6 @@ class Module(XModule): def get_html(self): return ''.format(item_id=self.item_id) - def __init__(self, xml, item_id, ajax_url=None, track_url=None, state=None, render_function = None): - XModule.__init__(self, xml, item_id, ajax_url, track_url, state, render_function) + def __init__(self, system, xml, item_id, state=None): + XModule.__init__(self, system, xml, item_id, state) diff --git a/djangoapps/courseware/modules/seq_module.py b/djangoapps/courseware/modules/seq_module.py index 80e0b4c7bf..161c95b604 100644 --- a/djangoapps/courseware/modules/seq_module.py +++ b/djangoapps/courseware/modules/seq_module.py @@ -2,9 +2,7 @@ import json from lxml import etree -## TODO: Abstract out from Django -from django.http import Http404 -from mitxmako.shortcuts import render_to_string +from mitxmako.shortcuts import render_to_response, render_to_string from x_module import XModule @@ -37,12 +35,10 @@ class Module(XModule): return self.destroy_js def handle_ajax(self, dispatch, get): - print "GET", get - print "DISPATCH", dispatch if dispatch=='goto_position': self.position = int(get['position']) return json.dumps({'success':True}) - raise Http404() + raise self.system.exception404 def render(self): if self.rendered: @@ -106,9 +102,8 @@ class Module(XModule): self.rendered = True - - def __init__(self, xml, item_id, ajax_url=None, track_url=None, state=None, track_function=None, render_function = None): - XModule.__init__(self, xml, item_id, ajax_url, track_url, state, track_function, render_function) + def __init__(self, system, xml, item_id, state=None): + XModule.__init__(self, system, xml, item_id, state) self.xmltree=etree.fromstring(xml) self.position = 1 diff --git a/djangoapps/courseware/modules/template_module.py b/djangoapps/courseware/modules/template_module.py index d9dbb613f0..41556eb9d4 100644 --- a/djangoapps/courseware/modules/template_module.py +++ b/djangoapps/courseware/modules/template_module.py @@ -14,16 +14,16 @@ class Module(XModule): @classmethod def get_xml_tags(c): + ## TODO: Abstract out from filesystem tags = os.listdir(settings.DATA_DIR+'/custom_tags') return tags def get_html(self): return self.html - def __init__(self, xml, item_id, ajax_url=None, track_url=None, state=None, track_function=None, render_function = None): - XModule.__init__(self, xml, item_id, ajax_url, track_url, state, track_function, render_function) + def __init__(self, system, xml, item_id, state=None): + XModule.__init__(self, system, xml, item_id, state) xmltree = etree.fromstring(xml) filename = xmltree.tag params = dict(xmltree.items()) -# print params self.html = render_to_string(filename, params, namespace = 'custom_tags') diff --git a/djangoapps/courseware/modules/vertical_module.py b/djangoapps/courseware/modules/vertical_module.py index c068cb9a76..f64e45fe7f 100644 --- a/djangoapps/courseware/modules/vertical_module.py +++ b/djangoapps/courseware/modules/vertical_module.py @@ -1,7 +1,5 @@ import json -## TODO: Abstract out from Django -from django.conf import settings from mitxmako.shortcuts import render_to_response, render_to_string from x_module import XModule @@ -26,8 +24,9 @@ class Module(XModule): def get_destroy_js(self): return self.destroy_js_text - def __init__(self, xml, item_id, ajax_url=None, track_url=None, state=None, track_function=None, render_function = None): - XModule.__init__(self, xml, item_id, ajax_url, track_url, state, track_function, render_function) + + def __init__(self, system, xml, item_id, state=None): + XModule.__init__(self, system, xml, item_id, state) xmltree=etree.fromstring(xml) self.contents=[(e.get("name"),self.render_function(e)) \ for e in xmltree] diff --git a/djangoapps/courseware/modules/video_module.py b/djangoapps/courseware/modules/video_module.py index f116853447..c678838f2b 100644 --- a/djangoapps/courseware/modules/video_module.py +++ b/djangoapps/courseware/modules/video_module.py @@ -3,9 +3,7 @@ import logging from lxml import etree -## TODO: Abstract out from Django -from django.http import Http404 -from mitxmako.shortcuts import render_to_string +from mitxmako.shortcuts import render_to_response, render_to_string from x_module import XModule @@ -58,8 +56,8 @@ class Module(XModule): def get_destroy_js(self): return "videoDestroy(\"{0}\");".format(self.item_id)+self.annotations_destroy - def __init__(self, xml, item_id, ajax_url=None, track_url=None, state=None, track_function=None, render_function = None): - XModule.__init__(self, xml, item_id, ajax_url, track_url, state, track_function, render_function) + def __init__(self, system, xml, item_id, state=None): + XModule.__init__(self, system, xml, item_id, state) xmltree=etree.fromstring(xml) self.youtube = xmltree.get('youtube') self.name = xmltree.get('name') diff --git a/djangoapps/courseware/modules/x_module.py b/djangoapps/courseware/modules/x_module.py index 4a379253c4..b21894b36b 100644 --- a/djangoapps/courseware/modules/x_module.py +++ b/djangoapps/courseware/modules/x_module.py @@ -45,13 +45,14 @@ class XModule(object): get is a dictionary-like object ''' return "" - def __init__(self, xml, item_id, ajax_url=None, track_url=None, state=None, track_function=None, render_function = None): + def __init__(self, system, xml, item_id, track_url=None, state=None): ''' In most cases, you must pass state or xml''' self.xml = xml self.item_id = item_id - self.ajax_url = ajax_url - self.track_url = track_url self.state = state - self.tracker = track_function - self.render_function = render_function + self.ajax_url = system.ajax_url + self.tracker = system.track_function + self.filestore = system.filestore + self.render_function = system.render_function + self.system = system