Commit 0a79130b by Calen Pennington

Make problem previews work by saving problem state in the current request session

parent 408726e6
from util.json_request import expect_json from util.json_request import expect_json
import json import json
import logging
from collections import defaultdict
from django.http import HttpResponse, Http404 from django.http import HttpResponse, Http404
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
...@@ -15,6 +17,10 @@ from static_replace import replace_urls ...@@ -15,6 +17,10 @@ from static_replace import replace_urls
from mitxmako.shortcuts import render_to_response, render_to_string from mitxmako.shortcuts import render_to_response, render_to_string
from xmodule.modulestore.django import modulestore from xmodule.modulestore.django import modulestore
from xmodule_modifiers import replace_static_urls, wrap_xmodule from xmodule_modifiers import replace_static_urls, wrap_xmodule
from xmodule.exceptions import NotFoundError
from functools import partial
log = logging.getLogger(__name__)
# ==== Public views ================================================== # ==== Public views ==================================================
...@@ -103,7 +109,7 @@ def edit_item(request): ...@@ -103,7 +109,7 @@ def edit_item(request):
'js_module': item.js_module_name, 'js_module': item.js_module_name,
'category': item.category, 'category': item.category,
'name': item.name, 'name': item.name,
'previews': get_module_previews(item), 'previews': get_module_previews(request, item),
}) })
...@@ -124,16 +130,64 @@ def user_author_string(user): ...@@ -124,16 +130,64 @@ def user_author_string(user):
@login_required @login_required
def preview_dispatch(request, module_id, dispatch): def preview_dispatch(request, preview_id, location, dispatch=None):
""" """
Dispatch an AJAX action to a preview XModule Dispatch an AJAX action to a preview XModule
Expects a POST request, and passes the arguments to the module Expects a POST request, and passes the arguments to the module
module_id: The Location of the module to dispatch to preview_id (str): An identifier specifying which preview this module is used for
location: The Location of the module to dispatch to
dispatch: The action to execute dispatch: The action to execute
""" """
pass
instance_state, shared_state = load_preview_state(request, preview_id, location)
descriptor = modulestore().get_item(location)
instance = load_preview_module(request, preview_id, descriptor, instance_state, shared_state)
# 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
save_preview_state(request, preview_id, location, instance.get_instance_state(), instance.get_shared_state())
return HttpResponse(ajax_return)
def load_preview_state(request, preview_id, location):
"""
Load the state of a preview module from the request
preview_id (str): An identifier specifying which preview this module is used for
location: The Location of the module to dispatch to
"""
if 'preview_states' not in request.session:
request.session['preview_states'] = defaultdict(dict)
instance_state = request.session['preview_states'][preview_id, location].get('instance')
shared_state = request.session['preview_states'][preview_id, location].get('shared')
return 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
preview_id (str): An identifier specifying which preview this module is used for
location: The Location of the module to dispatch to
instance_state: The instance state to save
shared_state: The shared state to save
"""
if 'preview_states' not in request.session:
request.session['preview_states'] = defaultdict(dict)
request.session['preview_states'][preview_id, location]['instance'] = instance_state
request.session['preview_states'][preview_id, location]['shared'] = shared_state
def render_from_lms(template_name, dictionary, context=None, namespace='main'): def render_from_lms(template_name, dictionary, context=None, namespace='main'):
...@@ -143,54 +197,64 @@ def render_from_lms(template_name, dictionary, context=None, namespace='main'): ...@@ -143,54 +197,64 @@ def render_from_lms(template_name, dictionary, context=None, namespace='main'):
return render_to_string(template_name, dictionary, context, namespace="lms." + namespace) return render_to_string(template_name, dictionary, context, namespace="lms." + namespace)
def sample_module_system(descriptor): def preview_module_system(request, preview_id, descriptor):
""" """
Returns a ModuleSystem for the specified descriptor that is specialized for Returns a ModuleSystem for the specified descriptor that is specialized for
rendering module previews. rendering module previews.
request: The active django request
preview_id (str): An identifier specifying which preview this module is used for
descriptor: An XModuleDescriptor
""" """
return ModuleSystem( return ModuleSystem(
ajax_url=reverse('preview_dispatch', args=[descriptor.location.url(), '']), ajax_url=reverse('preview_dispatch', args=[preview_id, descriptor.location.url(), '']),
# TODO (cpennington): Do we want to track how instructors are using the preview problems? # TODO (cpennington): Do we want to track how instructors are using the preview problems?
track_function=lambda x: None, track_function=lambda type, event: None,
filestore=descriptor.system.resources_fs, filestore=descriptor.system.resources_fs,
get_module=get_sample_module, get_module=partial(get_preview_module, request, preview_id),
render_template=render_from_lms, render_template=render_from_lms,
debug=True, debug=True,
replace_urls=replace_urls replace_urls=replace_urls
) )
def get_sample_module(location): def get_preview_module(request, preview_id, location):
""" """
Returns a sample XModule at the specified location. The sample_data is chosen arbitrarily Returns a preview XModule at the specified location. The preview_data is chosen arbitrarily
from the set of sample data for the descriptor specified by Location from the set of preview data for the descriptor specified by Location
request: The active django request
preview_id (str): An identifier specifying which preview this module is used for
location: A Location location: A Location
""" """
descriptor = modulestore().get_item(location) descriptor = modulestore().get_item(location)
instance_state, shared_state = descriptor.get_sample_state()[0] instance_state, shared_state = descriptor.get_sample_state()[0]
return load_sample_module(descriptor, instance_state, shared_state) return load_preview_module(request, preview_id, descriptor, instance_state, shared_state)
def load_sample_module(descriptor, instance_state, shared_state): def load_preview_module(request, preview_id, descriptor, instance_state, shared_state):
""" """
Return a sample XModule instantiated from the supplied descriptor, instance_state, and shared_state Return a preview XModule instantiated from the supplied descriptor, instance_state, and shared_state
request: The active django request
preview_id (str): An identifier specifying which preview this module is used for
descriptor: An XModuleDescriptor descriptor: An XModuleDescriptor
instance_state: An instance state string instance_state: An instance state string
shared_state: A shared state string shared_state: A shared state string
""" """
system = sample_module_system(descriptor) system = preview_module_system(request, preview_id, descriptor)
module = descriptor.xmodule_constructor(system)(instance_state, shared_state) module = descriptor.xmodule_constructor(system)(instance_state, shared_state)
module.get_html = replace_static_urls( module.get_html = replace_static_urls(
wrap_xmodule(module.get_html, module, "xmodule_display.html"), wrap_xmodule(module.get_html, module, "xmodule_display.html"),
module.metadata['data_dir'] module.metadata['data_dir']
) )
save_preview_state(request, preview_id, descriptor.location.url(),
module.get_instance_state(), module.get_shared_state())
return module return module
def get_module_previews(descriptor): def get_module_previews(request, descriptor):
""" """
Returns a list of preview XModule html contents. One preview is returned for each Returns a list of preview XModule html contents. One preview is returned for each
pair of states returned by get_sample_state() for the supplied descriptor. pair of states returned by get_sample_state() for the supplied descriptor.
...@@ -198,8 +262,8 @@ def get_module_previews(descriptor): ...@@ -198,8 +262,8 @@ def get_module_previews(descriptor):
descriptor: An XModuleDescriptor descriptor: An XModuleDescriptor
""" """
preview_html = [] preview_html = []
for instance_state, shared_state in descriptor.get_sample_state(): for idx, (instance_state, shared_state) in enumerate(descriptor.get_sample_state()):
module = load_sample_module(descriptor, instance_state, shared_state) module = load_preview_module(request, str(idx), descriptor, instance_state, shared_state)
preview_html.append(module.get_html()) preview_html.append(module.get_html())
return preview_html return preview_html
...@@ -225,6 +289,6 @@ def save_item(request): ...@@ -225,6 +289,6 @@ def save_item(request):
export_to_github(course, "CMS Edit", author_string) export_to_github(course, "CMS Edit", author_string)
descriptor = modulestore().get_item(item_location) descriptor = modulestore().get_item(item_location)
preview_html = get_module_previews(descriptor) preview_html = get_module_previews(request, descriptor)
return HttpResponse(json.dumps(preview_html)) return HttpResponse(json.dumps(preview_html))
...@@ -14,7 +14,8 @@ urlpatterns = ('', ...@@ -14,7 +14,8 @@ urlpatterns = ('',
url(r'^(?P<org>[^/]+)/(?P<course>[^/]+)/course/(?P<name>[^/]+)$', url(r'^(?P<org>[^/]+)/(?P<course>[^/]+)/course/(?P<name>[^/]+)$',
'contentstore.views.course_index', name='course_index'), 'contentstore.views.course_index', name='course_index'),
url(r'^github_service_hook$', 'github_sync.views.github_post_receive'), url(r'^github_service_hook$', 'github_sync.views.github_post_receive'),
url(r'^preview/modx/(?P<location>.*?)/(?P<dispatch>[^/]*)$', 'contentstore.views.preview_dispatch', name='preview_dispatch') url(r'^preview/modx/(?P<preview_id>[^/]*)/(?P<location>.*?)/(?P<dispatch>[^/]*)$',
'contentstore.views.preview_dispatch', name='preview_dispatch')
) )
# User creation and updating views # User creation and updating views
......
...@@ -21,9 +21,3 @@ ...@@ -21,9 +21,3 @@
% for chapter in toc: % for chapter in toc:
${make_chapter(chapter)} ${make_chapter(chapter)}
% endfor % endfor
## add a link to allow course.xml to be reloaded from the git content repo
##% if settings.QUICKEDIT:
## <h3><a href="#">quickedit</a></h3>
## <ul><li><a href="${MITX_ROOT_URL}/quickedit/course.xml">gitreload</a></li></ul>
##% endif
...@@ -4,12 +4,6 @@ ...@@ -4,12 +4,6 @@
% if problem['weight'] != 1: % if problem['weight'] != 1:
: ${ problem['weight'] } points : ${ problem['weight'] } points
% endif % endif
% if settings.QUICKEDIT:
<section class="staff">
<a href=${MITX_ROOT_URL}/quickedit/${id}>Quick Edit Problem</a>
</section>
% endif
</h2> </h2>
<section class="problem"> <section class="problem">
......
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