Commit 4442754a by Piotr Mitros

Redundant DB queries eliminated. We're at about 5 hits per sequence w/o Askbot, and 10 with

parent 98e9d96c
...@@ -45,15 +45,25 @@ class StudentModule(models.Model): ...@@ -45,15 +45,25 @@ class StudentModule(models.Model):
('video','video'), ('video','video'),
('html','html'), ('html','html'),
) )
## These three are the key for the object
module_type = models.CharField(max_length=32, choices=MODULE_TYPES, default='problem') module_type = models.CharField(max_length=32, choices=MODULE_TYPES, default='problem')
module_id = models.CharField(max_length=255) # Filename for homeworks, etc. module_id = models.CharField(max_length=255) # Filename for homeworks, etc.
student = models.ForeignKey(User) student = models.ForeignKey(User)
class Meta: class Meta:
unique_together = (('student', 'module_id', 'module_type'),) unique_together = (('student', 'module_id', 'module_type'),)
## Internal state of the object
state = models.TextField(null=True, blank=True) state = models.TextField(null=True, blank=True)
## Grade, and are we done?
grade = models.FloatField(null=True, blank=True) grade = models.FloatField(null=True, blank=True)
#max_grade = models.FloatField(null=True, blank=True)
# DONE_TYPES = (('done','DONE'), # Finished
# ('incomplete','NOTDONE'), # Not finished
# ('na','NA')) # Not applicable (e.g. vertical)
# done = models.CharField(max_length=16, choices=DONE_TYPES)
created = models.DateTimeField(auto_now_add=True) created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True) modified = models.DateTimeField(auto_now=True)
......
...@@ -4,6 +4,7 @@ from djangomako.shortcuts import render_to_response, render_to_string ...@@ -4,6 +4,7 @@ from djangomako.shortcuts import render_to_response, render_to_string
import json, os, sys import json, os, sys
from django.core.context_processors import csrf from django.core.context_processors import csrf
from django.db import connection
from django.template import Context from django.template import Context
from django.contrib.auth.models import User from django.contrib.auth.models import User
from auth.models import UserProfile from auth.models import UserProfile
...@@ -44,6 +45,16 @@ modx_modules={'problem':courseware.modules.capa_module.LoncapaModule, ...@@ -44,6 +45,16 @@ modx_modules={'problem':courseware.modules.capa_module.LoncapaModule,
'sequential':courseware.modules.seq_module.SequentialModule, 'sequential':courseware.modules.seq_module.SequentialModule,
'schematic':courseware.modules.schematic_module.SchematicModule} 'schematic':courseware.modules.schematic_module.SchematicModule}
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
# is broken.
for o in cache:
if o.module_type == module_type and \
o.module_id == module_id:
return o
return None
def make_track_function(request): def make_track_function(request):
def f(event_type, event): def f(event_type, event):
return track.views.server_track(request, event_type, event, page='x_module') return track.views.server_track(request, event_type, event, page='x_module')
...@@ -52,7 +63,6 @@ def make_track_function(request): ...@@ -52,7 +63,6 @@ def make_track_function(request):
def modx_dispatch(request, module=None, dispatch=None, id=None): def modx_dispatch(request, module=None, dispatch=None, id=None):
''' Generic view for extensions. ''' ''' Generic view for extensions. '''
# Grab the student information for the module from the database # Grab the student information for the module from the database
print module, request.user, id
s = StudentModule.objects.filter(module_type=module, s = StudentModule.objects.filter(module_type=module,
student=request.user, student=request.user,
module_id=id) module_id=id)
...@@ -77,8 +87,7 @@ def modx_dispatch(request, module=None, dispatch=None, id=None): ...@@ -77,8 +87,7 @@ def modx_dispatch(request, module=None, dispatch=None, id=None):
ajax_url=ajax_url, ajax_url=ajax_url,
state=s.state, state=s.state,
track_function = make_track_function(request), track_function = make_track_function(request),
render_function = render_module, render_function = None)
meta = request)
# Let the module handle the AJAX # Let the module handle the AJAX
ajax_return=instance.handle_ajax(dispatch, request.POST) ajax_return=instance.handle_ajax(dispatch, request.POST)
# Save the state back to the database # Save the state back to the database
...@@ -89,7 +98,7 @@ def modx_dispatch(request, module=None, dispatch=None, id=None): ...@@ -89,7 +98,7 @@ def modx_dispatch(request, module=None, dispatch=None, id=None):
# Return whatever the module wanted to return to the client/caller # Return whatever the module wanted to return to the client/caller
return HttpResponse(ajax_return) return HttpResponse(ajax_return)
def render_x_module(request, xml_module): def render_x_module(user, request, xml_module, module_object_preload):
''' Generic module for extensions. This renders to HTML. ''' ''' Generic module for extensions. This renders to HTML. '''
# Check if problem has an instance in DB # Check if problem has an instance in DB
module_type=xml_module.tag module_type=xml_module.tag
...@@ -97,13 +106,16 @@ def render_x_module(request, xml_module): ...@@ -97,13 +106,16 @@ def render_x_module(request, xml_module):
module_id=xml_module.get('id') #module_class.id_attribute) or "" module_id=xml_module.get('id') #module_class.id_attribute) or ""
# Grab state from database # Grab state from database
s = StudentModule.objects.filter(student=request.user, s = object_cache(module_object_preload,
module_id=module_id, user,
module_type = module_type) module_type,
if len(s) == 0: # If nothing in the database... module_id)#student=request.user,
#module_id=module_id,
#module_type = module_type)
if s == None: # If nothing in the database...
state=None state=None
else: else:
smod = s[0] smod = s
state = smod.state state = smod.state
# Create a new instance # Create a new instance
...@@ -113,27 +125,25 @@ def render_x_module(request, xml_module): ...@@ -113,27 +125,25 @@ def render_x_module(request, xml_module):
ajax_url=ajax_url, ajax_url=ajax_url,
state=state, state=state,
track_function = make_track_function(request), track_function = make_track_function(request),
render_function = render_module, render_function = lambda x: render_module(user, request, x, module_object_preload))
meta = request)
# If instance wasn't already in the database, create it # If instance wasn't already in the database, create it
if len(s) == 0: if s == None:
smod=StudentModule(student=request.user, smod=StudentModule(student=user,
module_type = module_type, module_type = module_type,
module_id=module_id, module_id=module_id,
state=instance.get_state()) state=instance.get_state())
smod.save() # This may be optional (at least in the case of no instance in the dB)
# Grab content # Grab content
content = {'content':instance.get_html(), content = {'content':instance.get_html(),
"destroy_js":instance.get_destroy_js(), "destroy_js":instance.get_destroy_js(),
'init_js':instance.get_init_js(), 'init_js':instance.get_init_js(),
'type':module_type} 'type':module_type}
smod.save() # This may be optional (at least in the case of no instance in the dB)
return content return content
def render_module(request, module): def render_module(user, request, module, module_object_preload):
''' Generic dispatch for internal modules. ''' ''' Generic dispatch for internal modules. '''
if module==None : if module==None :
return {"content":""} return {"content":""}
return render_x_module(request, module) return render_x_module(user, request, module, module_object_preload)
...@@ -25,7 +25,7 @@ class HtmlModule(XModule): ...@@ -25,7 +25,7 @@ class HtmlModule(XModule):
textlist=[i for i in textlist if type(i)==str] textlist=[i for i in textlist if type(i)==str]
return "".join(textlist) return "".join(textlist)
def __init__(self, xml, item_id, ajax_url=None, track_url=None, state=None, track_function=None, render_function = None, meta = None): 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) XModule.__init__(self, xml, item_id, ajax_url, track_url, state, track_function, render_function)
xmltree=etree.fromstring(xml) xmltree=etree.fromstring(xml)
self.filename = None self.filename = None
......
...@@ -18,6 +18,6 @@ class SchematicModule(XModule): ...@@ -18,6 +18,6 @@ class SchematicModule(XModule):
def get_html(self): def get_html(self):
return '<input type="hidden" class="schematic" name="{item_id}" height="480" width="640">'.format(item_id=self.item_id) return '<input type="hidden" class="schematic" name="{item_id}" height="480" width="640">'.format(item_id=self.item_id)
def __init__(self, xml, item_id, ajax_url=None, track_url=None, state=None, render_function = None, meta = None): 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) XModule.__init__(self, xml, item_id, ajax_url, track_url, state, render_function)
...@@ -36,7 +36,7 @@ class SequentialModule(XModule): ...@@ -36,7 +36,7 @@ class SequentialModule(XModule):
return json.dumps({'success':True}) return json.dumps({'success':True})
raise Http404() raise Http404()
def __init__(self, xml, item_id, ajax_url=None, track_url=None, state=None, track_function=None, render_function = None, meta = None): 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) XModule.__init__(self, xml, item_id, ajax_url, track_url, state, track_function, render_function)
xmltree=etree.fromstring(xml) xmltree=etree.fromstring(xml)
...@@ -60,7 +60,7 @@ class SequentialModule(XModule): ...@@ -60,7 +60,7 @@ class SequentialModule(XModule):
'init_js':m['init_js'], 'init_js':m['init_js'],
'type':m['type']} 'type':m['type']}
contents=[(e.get("name"),j(render_function(meta, e))) \ contents=[(e.get("name"),j(render_function(e))) \
for e in xmltree] for e in xmltree]
js="" js=""
......
...@@ -25,10 +25,10 @@ class VerticalModule(XModule): ...@@ -25,10 +25,10 @@ class VerticalModule(XModule):
def get_destroy_js(self): def get_destroy_js(self):
return self.destroy_js_text 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, meta = None): 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) XModule.__init__(self, xml, item_id, ajax_url, track_url, state, track_function, render_function)
xmltree=etree.fromstring(xml) xmltree=etree.fromstring(xml)
self.contents=[(e.get("name"),self.render_function(meta, e)) \ self.contents=[(e.get("name"),self.render_function(e)) \
for e in xmltree] for e in xmltree]
self.init_js_text="".join([e[1]['init_js'] for e in self.contents if 'init_js' in e[1]]) self.init_js_text="".join([e[1]['init_js'] for e in self.contents if 'init_js' in e[1]])
self.destroy_js_text="".join([e[1]['destroy_js'] for e in self.contents if 'destroy_js' in e[1]]) self.destroy_js_text="".join([e[1]['destroy_js'] for e in self.contents if 'destroy_js' in e[1]])
...@@ -51,7 +51,7 @@ class VideoModule(XModule): ...@@ -51,7 +51,7 @@ class VideoModule(XModule):
def get_destroy_js(self): def get_destroy_js(self):
return "videoDestroy(\""+self.item_id+"\");" return "videoDestroy(\""+self.item_id+"\");"
def __init__(self, xml, item_id, ajax_url=None, track_url=None, state=None, track_function=None, render_function = None, meta = None): 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) XModule.__init__(self, xml, item_id, ajax_url, track_url, state, track_function, render_function)
self.youtube = etree.XML(xml).get('youtube') self.youtube = etree.XML(xml).get('youtube')
self.position = 0 self.position = 0
......
...@@ -39,7 +39,7 @@ class XModule(object): ...@@ -39,7 +39,7 @@ class XModule(object):
get is a dictionary-like object ''' get is a dictionary-like object '''
return "" return ""
def __init__(self, xml, item_id, ajax_url=None, track_url=None, state=None, track_function=None, render_function = None, meta = None): def __init__(self, xml, item_id, ajax_url=None, track_url=None, state=None, track_function=None, render_function = None):
''' In most cases, you must pass state or xml''' ''' In most cases, you must pass state or xml'''
self.xml = xml self.xml = xml
self.item_id = item_id self.item_id = item_id
...@@ -48,4 +48,4 @@ class XModule(object): ...@@ -48,4 +48,4 @@ class XModule(object):
self.state = state self.state = state
self.tracker = track_function self.tracker = track_function
self.render_function = render_function self.render_function = render_function
self.meta = meta
...@@ -13,6 +13,8 @@ from django.http import HttpResponse, Http404 ...@@ -13,6 +13,8 @@ from django.http import HttpResponse, Http404
from django.shortcuts import redirect from django.shortcuts import redirect
from django.template import Context, loader from django.template import Context, loader
from djangomako.shortcuts import render_to_response, render_to_string from djangomako.shortcuts import render_to_response, render_to_string
from django.db import connection
from lxml import etree from lxml import etree
from auth.models import UserProfile from auth.models import UserProfile
...@@ -105,7 +107,8 @@ def render_accordion(request,course,chapter,section): ...@@ -105,7 +107,8 @@ def render_accordion(request,course,chapter,section):
def index(request, course="6.002 Spring 2012", chapter="Using the System", section="Hints"): def index(request, course="6.002 Spring 2012", chapter="Using the System", section="Hints"):
''' Displays courseware accordion, and any associated content. ''' Displays courseware accordion, and any associated content.
''' '''
if not settings.COURSEWARE_ENABLED or not request.user.is_authenticated(): user = request.user
if not settings.COURSEWARE_ENABLED or not user.is_authenticated():
return redirect('/') return redirect('/')
# Fixes URLs -- we don't get funny encoding characters from spaces # Fixes URLs -- we don't get funny encoding characters from spaces
...@@ -120,7 +123,7 @@ def index(request, course="6.002 Spring 2012", chapter="Using the System", secti ...@@ -120,7 +123,7 @@ def index(request, course="6.002 Spring 2012", chapter="Using the System", secti
if course!="6.002 Spring 2012": if course!="6.002 Spring 2012":
return redirect('/') return redirect('/')
dom = content_parser.course_file(request.user) dom = content_parser.course_file(user)
dom_module = dom.xpath("//course[@name=$course]/chapter[@name=$chapter]//section[@name=$section]/*[1]", dom_module = dom.xpath("//course[@name=$course]/chapter[@name=$chapter]//section[@name=$section]/*[1]",
course=course, chapter=chapter, section=section) course=course, chapter=chapter, section=section)
if len(dom_module) == 0: if len(dom_module) == 0:
...@@ -130,17 +133,21 @@ def index(request, course="6.002 Spring 2012", chapter="Using the System", secti ...@@ -130,17 +133,21 @@ def index(request, course="6.002 Spring 2012", chapter="Using the System", secti
accordion=render_accordion(request, course, chapter, section) accordion=render_accordion(request, course, chapter, section)
module=render_module(request, module) module_ids = dom.xpath("//course[@name=$course]/chapter[@name=$chapter]//section[@name=$section]//@id",
course=course, chapter=chapter, section=section)
if 'init_js' not in module: module_object_preload = StudentModule.objects.filter(student=user,
module['init_js']='' module_id__in=module_ids)
module=render_module(user, request, module, module_object_preload)
if 'init_js' not in module:
module['init_js']=''
context={'init':accordion['init_js']+module['init_js'], context={'init':accordion['init_js']+module['init_js'],
'accordion':accordion['content'], 'accordion':accordion['content'],
'content':module['content'], 'content':module['content'],
'csrf':csrf(request)['csrf_token']} 'csrf':csrf(request)['csrf_token']}
return render_to_response('courseware.html', context)
result = render_to_response('courseware.html', context)
return result
ASKBOT_ENABLED = False ASKBOT_ENABLED = False
execfile("settings.py") execfile("settings_old_askbot.py")
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