Commit 54ccffcb by Calen Pennington

Merge pull request #80 from MITx/pmitros/dexml

Pmitros/dexml
parents 725cbe14 b2477de7
......@@ -18,6 +18,7 @@ from models import StudentModule
from multicourse import multicourse_settings
import courseware.modules
import courseware.content_parser as content_parser
log = logging.getLogger("mitx.courseware")
......@@ -79,9 +80,8 @@ def grade_histogram(module_id):
return []
return grades
def render_x_module(user, request, xml_module, module_object_preload):
''' Generic module for extensions. This renders to HTML. '''
# Check if problem has an instance in DB
def get_module(user, request, xml_module, module_object_preload):
module_type=xml_module.tag
module_class=courseware.modules.get_module_class(module_type)
module_id=xml_module.get('id') #module_class.id_attribute) or ""
......@@ -110,7 +110,7 @@ def render_x_module(user, request, xml_module, module_object_preload):
ajax_url = settings.MITX_ROOT_URL + '/modx/'+module_type+'/'+module_id+'/'
system = I4xSystem(track_function = make_track_function(request),
render_function = lambda x: render_module(user, request, x, module_object_preload),
render_function = lambda x: render_x_module(user, request, x, module_object_preload),
ajax_url = ajax_url,
filestore = OSFS(data_root),
)
......@@ -129,6 +129,15 @@ def render_x_module(user, request, xml_module, module_object_preload):
smod.save()
module_object_preload.append(smod)
return (instance, smod, module_type)
def render_x_module(user, request, xml_module, module_object_preload):
''' Generic module for extensions. This renders to HTML. '''
if xml_module==None :
return {"content":""}
(instance, smod, module_type) = get_module(user, request, xml_module, module_object_preload)
# Grab content
content = instance.get_html()
......@@ -146,8 +155,76 @@ def render_x_module(user, request, xml_module, module_object_preload):
return content
def render_module(user, request, module, module_object_preload):
''' Generic dispatch for internal modules. '''
if module==None :
return {"content":""}
return render_x_module(user, request, module, module_object_preload)
def modx_dispatch(request, module=None, dispatch=None, id=None):
''' Generic view for extensions. '''
if not request.user.is_authenticated():
return redirect('/')
# Grab the student information for the module from the database
s = StudentModule.objects.filter(student=request.user,
module_id=id)
#s = StudentModule.get_with_caching(request.user, id)
if len(s) == 0 or s is None:
log.debug("Couldnt find module for user and id " + str(module) + " " + str(request.user) + " "+ str(id))
raise Http404
s = s[0]
oldgrade = s.grade
oldstate = s.state
dispatch=dispatch.split('?')[0]
ajax_url = settings.MITX_ROOT_URL + '/modx/'+module+'/'+id+'/'
# get coursename if stored
coursename = multicourse_settings.get_coursename_from_request(request)
if coursename and settings.ENABLE_MULTICOURSE:
xp = multicourse_settings.get_course_xmlpath(coursename) # path to XML for the course
data_root = settings.DATA_DIR + xp
else:
data_root = settings.DATA_DIR
# Grab the XML corresponding to the request from course.xml
try:
xml = content_parser.module_xml(request.user, module, 'id', id, coursename)
except:
log.exception("Unable to load module during ajax call")
if accepts(request, 'text/html'):
return render_to_response("module-error.html", {})
else:
response = HttpResponse(json.dumps({'success': "We're sorry, this module is temporarily unavailable. Our staff is working to fix it as soon as possible"}))
return response
# Create the module
system = I4xSystem(track_function = make_track_function(request),
render_function = None,
ajax_url = ajax_url,
filestore = OSFS(data_root),
)
try:
instance=courseware.modules.get_module_class(module)(system,
xml,
id,
state=oldstate)
except:
log.exception("Unable to load module instance during ajax call")
if accepts(request, 'text/html'):
return render_to_response("module-error.html", {})
else:
response = HttpResponse(json.dumps({'success': "We're sorry, this module is temporarily unavailable. Our staff is working to fix it as soon as possible"}))
return response
# Let the module handle the AJAX
ajax_return=instance.handle_ajax(dispatch, request.POST)
# Save the state back to the database
s.state=instance.get_state()
if instance.get_score():
s.grade=instance.get_score()['score']
if s.grade != oldgrade or s.state != oldstate:
s.save()
# Return whatever the module wanted to return to the client/caller
return HttpResponse(ajax_return)
......@@ -18,7 +18,7 @@ from lxml import etree
## TODO: Abstract out from Django
from mitxmako.shortcuts import render_to_string
from x_module import XModule
from x_module import XModule, XModuleDescriptor
from courseware.capa.capa_problem import LoncapaProblem, StudentInputError
import courseware.content_parser as content_parser
from multicourse import multicourse_settings
......@@ -31,6 +31,9 @@ class ComplexEncoder(json.JSONEncoder):
return "{real:.7g}{imag:+.7g}*j".format(real = obj.real,imag = obj.imag)
return json.JSONEncoder.default(self, obj)
class ModuleDescriptor(XModuleDescriptor):
pass
class Module(XModule):
''' Interface between capa_problem and x_module. Originally a hack
meant to be refactored out, but it seems to be serving a useful
......
......@@ -2,9 +2,12 @@ import json
from mitxmako.shortcuts import render_to_response, render_to_string
from x_module import XModule
from x_module import XModule, XModuleDescriptor
from lxml import etree
class ModuleDescriptor(XModuleDescriptor):
pass
class Module(XModule):
id_attribute = 'filename'
......
......@@ -4,7 +4,10 @@ import json
from django.conf import settings
from mitxmako.shortcuts import render_to_response, render_to_string
from x_module import XModule
from x_module import XModule, XModuleDescriptor
class ModuleDescriptor(XModuleDescriptor):
pass
class Module(XModule):
id_attribute = 'id'
......
......@@ -4,12 +4,15 @@ from lxml import etree
from mitxmako.shortcuts import render_to_string
from x_module import XModule
from x_module import XModule, XModuleDescriptor
# HACK: This shouldn't be hard-coded to two types
# OBSOLETE: This obsoletes 'type'
class_priority = ['video', 'problem']
class ModuleDescriptor(XModuleDescriptor):
pass
class Module(XModule):
''' Layout module which lays out content in a temporal sequence
'''
......@@ -37,24 +40,13 @@ class Module(XModule):
def render(self):
if self.rendered:
return
def j(m):
''' jsonify contents so it can be embedded in a js array
We also need to split </script> tags so they don't break
mid-string'''
content=json.dumps(m['content'])
content=content.replace('</script>', '<"+"/script>')
return {'content':content,
'type': m['type']}
## Returns a set of all types of all sub-children
child_classes = [set([i.tag for i in e.iter()]) for e in self.xmltree]
titles = ["\n".join([i.get("name").strip() for i in e.iter() if i.get("name") is not None]) \
for e in self.xmltree]
self.contents = [j(self.render_function(e)) for e in self.xmltree]
self.contents = self.rendered_children()
for contents, title in zip(self.contents, titles):
contents['title'] = title
......@@ -66,19 +58,15 @@ class Module(XModule):
new_class = c
content['type'] = new_class
params={'items':self.contents,
# Split </script> tags -- browsers handle this as end
# of script, even if it occurs mid-string. Do this after json.dumps()ing
# so that we can be sure of the quotations being used
params={'items':json.dumps(self.contents).replace('</script>', '<"+"/script>'),
'id':self.item_id,
'position': self.position,
'titles':titles,
'tag':self.xmltree.tag}
# TODO/BUG: Destroy JavaScript should only be called for the active view
# This calls it for all the views
#
# To fix this, we'd probably want to have some way of assigning unique
# IDs to sequences.
destroy_js="".join([e['destroy_js'] for e in self.contents if 'destroy_js' in e])
if self.xmltree.tag in ['sequential', 'videosequence']:
self.content=render_to_string('seq_module.html',params)
if self.xmltree.tag == 'tab':
......
......@@ -3,9 +3,12 @@ import os
from mitxmako.shortcuts import render_to_response, render_to_string
from x_module import XModule
from x_module import XModule, XModuleDescriptor
from lxml import etree
class ModuleDescriptor(XModuleDescriptor):
pass
class Module(XModule):
def get_state(self):
return json.dumps({ })
......
......@@ -2,9 +2,12 @@ import json
from mitxmako.shortcuts import render_to_response, render_to_string
from x_module import XModule
from x_module import XModule, XModuleDescriptor
from lxml import etree
class ModuleDescriptor(XModuleDescriptor):
pass
class Module(XModule):
id_attribute = 'id'
......
......@@ -5,10 +5,13 @@ from lxml import etree
from mitxmako.shortcuts import render_to_response, render_to_string
from x_module import XModule
from x_module import XModule, XModuleDescriptor
log = logging.getLogger("mitx.courseware.modules")
class ModuleDescriptor(XModuleDescriptor):
pass
class Module(XModule):
id_attribute = 'youtube'
video_time = 0
......
from lxml import etree
import courseware.progress
def dummy_track(event_type, event):
......@@ -24,6 +26,21 @@ class XModule(object):
or a CAPA input type '''
return ['xmodule']
def get_name():
name = self.__xmltree.get(name)
if name:
return name
else:
raise "We should iterate through children and find a default name"
def rendered_children(self):
'''
Render all children.
This really ought to return a list of xmodules, instead of dictionaries
'''
children = [self.render_function(e) for e in self.__xmltree]
return children
def __init__(self, system = None, xml = None, item_id = None,
json = None, track_url=None, state=None):
''' In most cases, you must pass state or xml'''
......@@ -38,6 +55,8 @@ class XModule(object):
self.json = json
self.item_id = item_id
self.state = state
self.__xmltree = etree.fromstring(xml) # PRIVATE
if system:
## These are temporary; we really should go
......@@ -83,14 +102,24 @@ class XModule(object):
get is a dictionary-like object '''
return ""
### Functions used in the CMS
class XModuleDescriptor(object):
def __init__(self, xml = None, json = None):
if not xml and not json:
raise "XModuleDescriptor must be initalized with XML or JSON"
if not xml:
raise NotImplementedError("Code does not have support for JSON yet")
self.xml = xml
self.json = json
def get_xml(self):
''' For conversions between JSON and legacy XML representations.
'''
if self.xml:
return self.xml
else:
raise NotImplementedError
raise NotImplementedError("JSON->XML Translation not implemented")
def get_json(self):
''' For conversions between JSON and legacy XML representations.
......@@ -99,14 +128,14 @@ class XModule(object):
raise NotImplementedError
return self.json # TODO: Return context as well -- files, etc.
else:
raise NotImplementedError
raise NotImplementedError("XML->JSON Translation not implemented")
def handle_cms_json(self):
raise NotImplementedError
#def handle_cms_json(self):
# raise NotImplementedError
def render(self, size):
''' Size: [thumbnail, small, full]
Small ==> what we drag around
Full ==> what we edit
'''
raise NotImplementedError
#def render(self, size):
# ''' Size: [thumbnail, small, full]
# Small ==> what we drag around
# Full ==> what we edit
# '''
# raise NotImplementedError
import logging
import urllib
import json
from fs.osfs import OSFS
......@@ -8,7 +7,7 @@ from django.conf import settings
from django.core.context_processors import csrf
from django.contrib.auth.models import User
from django.contrib.auth.decorators import login_required
from django.http import Http404, HttpResponse
from django.http import Http404
from django.shortcuts import redirect
from mitxmako.shortcuts import render_to_response, render_to_string
#from django.views.decorators.csrf import ensure_csrf_cookie
......@@ -16,10 +15,9 @@ from django.views.decorators.cache import cache_control
from lxml import etree
from module_render import render_module, make_track_function, I4xSystem
from module_render import render_x_module, make_track_function, I4xSystem
from models import StudentModule
from student.models import UserProfile
from util.views import accepts
from multicourse import multicourse_settings
import courseware.content_parser as content_parser
......@@ -129,7 +127,7 @@ def render_section(request, section):
module_object_preload = []
try:
module = render_module(user, request, dom, module_object_preload)
module = render_x_module(user, request, dom, module_object_preload)
except:
log.exception("Unable to load module")
context.update({
......@@ -219,7 +217,7 @@ def index(request, course=None, chapter="Using the System", section="Hints"):
}
try:
module = render_module(user, request, module, module_object_preload)
module = render_x_module(user, request, module, module_object_preload)
except:
log.exception("Unable to load module")
context.update({
......@@ -237,78 +235,6 @@ def index(request, course=None, chapter="Using the System", section="Hints"):
return result
def modx_dispatch(request, module=None, dispatch=None, id=None):
''' Generic view for extensions. '''
if not request.user.is_authenticated():
return redirect('/')
# Grab the student information for the module from the database
s = StudentModule.objects.filter(student=request.user,
module_id=id)
#s = StudentModule.get_with_caching(request.user, id)
if len(s) == 0 or s is None:
log.debug("Couldnt find module for user and id " + str(module) + " " + str(request.user) + " "+ str(id))
raise Http404
s = s[0]
oldgrade = s.grade
oldstate = s.state
dispatch=dispatch.split('?')[0]
ajax_url = settings.MITX_ROOT_URL + '/modx/'+module+'/'+id+'/'
# get coursename if stored
coursename = multicourse_settings.get_coursename_from_request(request)
if coursename and settings.ENABLE_MULTICOURSE:
xp = multicourse_settings.get_course_xmlpath(coursename) # path to XML for the course
data_root = settings.DATA_DIR + xp
else:
data_root = settings.DATA_DIR
# Grab the XML corresponding to the request from course.xml
try:
xml = content_parser.module_xml(request.user, module, 'id', id, coursename)
except:
log.exception("Unable to load module during ajax call")
if accepts(request, 'text/html'):
return render_to_response("module-error.html", {})
else:
response = HttpResponse(json.dumps({'success': "We're sorry, this module is temporarily unavailable. Our staff is working to fix it as soon as possible"}))
return response
# Create the module
system = I4xSystem(track_function = make_track_function(request),
render_function = None,
ajax_url = ajax_url,
filestore = OSFS(data_root),
)
try:
instance=courseware.modules.get_module_class(module)(system,
xml,
id,
state=oldstate)
except:
log.exception("Unable to load module instance during ajax call")
if accepts(request, 'text/html'):
return render_to_response("module-error.html", {})
else:
response = HttpResponse(json.dumps({'success': "We're sorry, this module is temporarily unavailable. Our staff is working to fix it as soon as possible"}))
return response
# Let the module handle the AJAX
ajax_return=instance.handle_ajax(dispatch, request.POST)
# Save the state back to the database
s.state=instance.get_state()
if instance.get_score():
s.grade=instance.get_score()['score']
if s.grade != oldgrade or s.state != oldstate:
s.save()
# Return whatever the module wanted to return to the client/caller
return HttpResponse(ajax_return)
def quickedit(request, id=None):
'''
quick-edit capa problem.
......
django-admin.py runserver --settings=envs.dev --pythonpath=.
django-admin.py runserver --settings=envs.dev --pythonpath=. || django-admin runserver --settings=envs.dev --pythonpath=.
\ No newline at end of file
......@@ -39,7 +39,7 @@ class @Sequence
$.postWithPrefix "/modx/#{@tag}/#{@id}/goto_position", position: new_position
@mark_active new_position
@$('#seq_content').html eval(@elements[new_position - 1].content)
@$('#seq_content').html @elements[new_position - 1].content
MathJax.Hub.Queue(["Typeset", MathJax.Hub])
@position = new_position
......
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