Factored out a couple of modules
--HG-- rename : courseware/static/schematic.js => courseware/static/js/schematic.js
This commit is contained in:
@@ -5,13 +5,13 @@ import uuid
|
||||
class UserProfile(models.Model):
|
||||
## CRITICAL TODO/SECURITY
|
||||
# Sanitize all fields.
|
||||
# This is not visible to other users, but could introduce holes
|
||||
# later
|
||||
# This is not visible to other users, but could introduce holes later
|
||||
user = models.ForeignKey(User, unique=True, db_index=True)
|
||||
name = models.TextField(blank=True)
|
||||
language = models.TextField(blank=True)
|
||||
location = models.TextField(blank=True)
|
||||
meta = models.TextField(blank=True) # JSON dictionary for future expansion
|
||||
courseware = models.TextField(blank=True)
|
||||
|
||||
class Registration(models.Model):
|
||||
''' Allows us to wait for e-mail before user is registered. A
|
||||
|
||||
@@ -6,6 +6,10 @@ from x_module import XModule
|
||||
|
||||
from capa_problem import LoncapaProblem
|
||||
|
||||
import dateutil
|
||||
import datetime
|
||||
|
||||
|
||||
from xml.dom.minidom import parse, parseString
|
||||
|
||||
## TODO: Abstract out from Django
|
||||
@@ -18,8 +22,13 @@ class LoncapaModule(XModule):
|
||||
prupose now. We can e.g .destroy and create the capa_problem on a
|
||||
reset.
|
||||
'''
|
||||
xml_tags=["problem"]
|
||||
id_attribute="filename"
|
||||
xml_tags = ["problem"]
|
||||
id_attribute = "filename"
|
||||
|
||||
attempts = None
|
||||
max_attempts = None
|
||||
|
||||
due_date = None
|
||||
|
||||
def get_state(self):
|
||||
return self.lcp.get_state()
|
||||
@@ -66,18 +75,33 @@ class LoncapaModule(XModule):
|
||||
XModule.__init__(self, xml, item_id, ajax_url, track_url, state)
|
||||
dom=parseString(xml)
|
||||
node=dom.childNodes[0]
|
||||
|
||||
self.due_date=node.getAttribute("due")
|
||||
if len(self.due_date)>0:
|
||||
self.due_date=dateutil.parser.parse(self.due_date)
|
||||
else:
|
||||
self.due_date=None
|
||||
|
||||
self.max_attempts=node.getAttribute("attempts")
|
||||
if len(self.max_attempts)>0:
|
||||
self.max_attempts=int(self.max_attempts)
|
||||
else:
|
||||
self.max_attempts=None
|
||||
|
||||
self.filename=node.getAttribute("filename")
|
||||
filename=settings.DATA_DIR+self.filename+".xml"
|
||||
self.name=node.getAttribute("name")
|
||||
self.lcp=LoncapaProblem(filename, self.item_id, state)
|
||||
|
||||
def handle_ajax(self, dispatch, get):
|
||||
if dispatch=='problem_check':
|
||||
if dispatch=='problem_get':
|
||||
response = self.get_problem(get)
|
||||
elif False: #self.due_date >
|
||||
return json.dumps({"error":"Past due date"})
|
||||
elif dispatch=='problem_check':
|
||||
response = self.check_problem(get)
|
||||
elif dispatch=='problem_reset':
|
||||
response = self.reset_problem(get)
|
||||
elif dispatch=='problem_get':
|
||||
response = self.get_problem(get)
|
||||
else:
|
||||
return "Error"
|
||||
return response
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import random, numpy, math, scipy, sys, StringIO, os, struct, json
|
||||
from dateutil import parser
|
||||
|
||||
from xml.dom.minidom import parse, parseString
|
||||
|
||||
@@ -198,6 +199,7 @@ class LoncapaProblem():
|
||||
return html
|
||||
|
||||
def grade_fr(self, question, answer):
|
||||
print question, answer
|
||||
correct = True
|
||||
for i in range(question['samples_count']):
|
||||
instructor_variables = strip_dict(dict(self.context))
|
||||
@@ -208,6 +210,9 @@ class LoncapaProblem():
|
||||
student_variables[str(var)] = value
|
||||
instructor_result = evaluator(instructor_variables,{},str(question['answer']))
|
||||
student_result = evaluator(student_variables,{},str(answer))
|
||||
print student_result, instructor_result
|
||||
if math.isnan(student_result) or math.isinf(student_result):
|
||||
return "incorrect"
|
||||
if abs( student_result - instructor_result ) > question['tolerance']:
|
||||
return "incorrect"
|
||||
|
||||
|
||||
33
courseware/html_module.py
Normal file
33
courseware/html_module.py
Normal file
@@ -0,0 +1,33 @@
|
||||
from x_module import XModule
|
||||
|
||||
from xml.dom.minidom import parse, parseString
|
||||
|
||||
import json
|
||||
|
||||
## TODO: Abstract out from Django
|
||||
from django.conf import settings
|
||||
from djangomako.shortcuts import render_to_response, render_to_string
|
||||
|
||||
class HtmlModule(XModule):
|
||||
id_attribute = 'filename'
|
||||
|
||||
def get_state(self):
|
||||
return json.dumps({ })
|
||||
|
||||
def get_xml_tags():
|
||||
return "html"
|
||||
|
||||
def get_html(self):
|
||||
print "XX",self.item_id
|
||||
return render_to_string(self.item_id, {'id': self.item_id})
|
||||
|
||||
def __init__(self, xml, item_id, ajax_url=None, track_url=None, state=None):
|
||||
print "item id" , item_id
|
||||
XModule.__init__(self, xml, item_id, ajax_url, track_url, state)
|
||||
|
||||
# template_source=module.getAttribute('filename')
|
||||
# return {'content':render_to_string(template_source, {})}
|
||||
|
||||
# print state
|
||||
# if state!=None and "time" not in json.loads(state):
|
||||
# self.video_time = 0
|
||||
166
courseware/module_render.py
Normal file
166
courseware/module_render.py
Normal file
@@ -0,0 +1,166 @@
|
||||
from django.http import HttpResponse
|
||||
from django.template import Context, loader
|
||||
from djangomako.shortcuts import render_to_response, render_to_string
|
||||
from xml.dom.minidom import parse, parseString
|
||||
import json, os, sys
|
||||
from django.core.context_processors import csrf
|
||||
|
||||
from django.template import Context
|
||||
from django.contrib.auth.models import User
|
||||
from auth.models import UserProfile
|
||||
from django.shortcuts import redirect
|
||||
|
||||
import StringIO
|
||||
|
||||
from django.http import Http404
|
||||
|
||||
import urllib
|
||||
|
||||
import capa_module
|
||||
import video_module
|
||||
import html_module
|
||||
|
||||
from models import StudentModule
|
||||
|
||||
import urllib
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
import content_parser
|
||||
|
||||
import uuid
|
||||
|
||||
#def html_module(request, module):
|
||||
# ''' Show basic text
|
||||
# '''
|
||||
# template_source=module.getAttribute('filename')
|
||||
# return {'content':render_to_string(template_source, {})}
|
||||
|
||||
def vertical_module(request, module):
|
||||
''' Layout module which lays out content vertically.
|
||||
'''
|
||||
contents=[(e.getAttribute("name"),render_module(request, e)) \
|
||||
for e in module.childNodes \
|
||||
if e.nodeType==1]
|
||||
js="".join([e[1]['init_js'] for e in contents if 'init_js' in e[1]])
|
||||
|
||||
return {'init_js':js,
|
||||
"destroy_js":"",
|
||||
'content':render_to_string('vert_module.html',{'items':contents})}
|
||||
|
||||
def seq_module(request, module):
|
||||
''' Layout module which lays out content in a temporal sequence
|
||||
'''
|
||||
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
|
||||
if 'init_js' not in m: m['init_js']=""
|
||||
content=json.dumps(m['content'])
|
||||
content=content.replace('</script>', '<"+"/script>')
|
||||
return {'content':content,
|
||||
"destroy_js":"",
|
||||
'init_js':m['init_js']}
|
||||
contents=[(e.getAttribute("name"),j(render_module(request, e))) \
|
||||
for e in module.childNodes \
|
||||
if e.nodeType==1]
|
||||
|
||||
js="".join([e[1]['init_js'] for e in contents if 'init_js' in e[1]])
|
||||
|
||||
iid=uuid.uuid1().hex
|
||||
|
||||
params={'items':contents,
|
||||
'id':"seq"}
|
||||
|
||||
print module.nodeName
|
||||
if module.nodeName == 'sequential':
|
||||
return {'init_js':js+render_to_string('seq_module.js',params),
|
||||
"destroy_js":"",
|
||||
'content':render_to_string('seq_module.html',params)}
|
||||
if module.nodeName == 'tab':
|
||||
return {'init_js':js+render_to_string('tab_module.js',params),
|
||||
"destroy_js":"",
|
||||
'content':render_to_string('tab_module.html',params)}
|
||||
|
||||
|
||||
modx_modules={'problem':capa_module.LoncapaModule,
|
||||
'video':video_module.VideoModule,
|
||||
'html':html_module.HtmlModule}
|
||||
|
||||
def render_x_module(request, xml_module):
|
||||
''' Generic module for extensions. This renders to HTML. '''
|
||||
# Check if problem has an instance in DB
|
||||
module_type=xml_module.nodeName
|
||||
module_class=modx_modules[module_type]
|
||||
print "mida",module_class.id_attribute
|
||||
module_id=xml_module.getAttribute(module_class.id_attribute)
|
||||
print "mid",module_id
|
||||
|
||||
# Grab state from database
|
||||
s = StudentModule.objects.filter(student=request.user,
|
||||
module_id=module_id,
|
||||
module_type = module_type)
|
||||
if len(s) == 0: # If nothing in the database...
|
||||
state=None
|
||||
else:
|
||||
smod = s[0]
|
||||
state = smod.state
|
||||
|
||||
# Create a new instance
|
||||
ajax_url = '/modx/'+module_type+'/'+module_id+'/'
|
||||
instance=module_class(xml_module.toxml(),
|
||||
module_id,
|
||||
ajax_url=ajax_url,
|
||||
state=state)
|
||||
|
||||
# If instance wasn't already in the database, create it
|
||||
if len(s) == 0:
|
||||
smod=StudentModule(student=request.user,
|
||||
module_type = module_type,
|
||||
module_id=module_id,
|
||||
state=instance.get_state(),
|
||||
xml=instance.xml)
|
||||
# Grab content
|
||||
content = {'content':instance.get_html(),
|
||||
"destroy_js":instance.get_destroy_js(),
|
||||
'init_js':instance.get_init_js()}
|
||||
|
||||
smod.save() # This may be optional (at least in the case of no instance in the dB)
|
||||
|
||||
return content
|
||||
|
||||
def modx_dispatch(request, module=None, dispatch=None, id=None):
|
||||
''' Generic module for extensions. '''
|
||||
s = StudentModule.objects.filter(module_type=module, student=request.user, module_id=id)
|
||||
if len(s) == 0:
|
||||
raise Http404
|
||||
|
||||
s=s[0]
|
||||
|
||||
dispatch=dispatch.split('?')[0]
|
||||
|
||||
ajax_url = '/modx/'+module+'/'+id+'/'
|
||||
|
||||
instance=modx_modules[module](s.xml, s.module_id, ajax_url=ajax_url, state=s.state)
|
||||
html=instance.handle_ajax(dispatch, request.GET)
|
||||
s.state=instance.get_state()
|
||||
s.grade=instance.get_score()['score']
|
||||
s.save()
|
||||
return HttpResponse(html)
|
||||
|
||||
module_types={'video':render_x_module,
|
||||
'html':render_x_module,
|
||||
'tab':seq_module,
|
||||
'vertical':vertical_module,
|
||||
'sequential':seq_module,
|
||||
'problem':render_x_module,
|
||||
}
|
||||
#'lab':lab_module,
|
||||
|
||||
def render_module(request, module):
|
||||
''' Generic dispatch for internal modules. '''
|
||||
if module==None:
|
||||
return {"content":""}
|
||||
if str(module.localName) in module_types:
|
||||
return module_types[module.localName](request, module)
|
||||
raise Http404
|
||||
48
courseware/profile.py
Normal file
48
courseware/profile.py
Normal file
@@ -0,0 +1,48 @@
|
||||
def profile(request):
|
||||
''' User profile. Show username, location, etc, as well as grades .
|
||||
We need to allow the user to change some of these settings .'''
|
||||
if not request.user.is_authenticated():
|
||||
return redirect('/')
|
||||
|
||||
dom=parse(settings.DATA_DIR+'course.xml')
|
||||
hw=[]
|
||||
course = dom.getElementsByTagName('course')[0]
|
||||
chapters = course.getElementsByTagName('chapter')
|
||||
|
||||
responses=StudentModule.objects.filter(student=request.user)
|
||||
|
||||
for c in chapters:
|
||||
for s in c.getElementsByTagName('section'):
|
||||
problems=s.getElementsByTagName('problem')
|
||||
scores=[]
|
||||
if len(problems)>0:
|
||||
for p in problems:
|
||||
id = p.getAttribute('filename')
|
||||
correct = 0
|
||||
for response in responses:
|
||||
if response.module_id == id:
|
||||
if response.grade!=None:
|
||||
correct=response.grade
|
||||
else:
|
||||
correct=0
|
||||
total=capa_module.LoncapaModule(p.toxml(), "id").max_score() # TODO: Add state. Not useful now, but maybe someday problems will have randomized max scores?
|
||||
scores.append((int(correct),total))
|
||||
score={'course':course.getAttribute('name'),
|
||||
'section':s.getAttribute("name"),
|
||||
'chapter':c.getAttribute("name"),
|
||||
'scores':scores,
|
||||
}
|
||||
hw.append(score)
|
||||
|
||||
user_info=UserProfile.objects.get(user=request.user)
|
||||
|
||||
context={'name':user_info.name,
|
||||
'username':request.user.username,
|
||||
'location':user_info.location,
|
||||
'language':user_info.language,
|
||||
'email':request.user.email,
|
||||
'homeworks':hw,
|
||||
'csrf':csrf(request)['csrf_token']
|
||||
}
|
||||
return render_to_response('profile.html', context)
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
# For calculator:
|
||||
# http://pyparsing.wikispaces.com/file/view/fourFn.py
|
||||
|
||||
from x_module import XModule
|
||||
|
||||
from xml.dom.minidom import parse, parseString
|
||||
@@ -12,10 +9,6 @@ from django.conf import settings
|
||||
from djangomako.shortcuts import render_to_response, render_to_string
|
||||
|
||||
class VideoModule(XModule):
|
||||
''' Implements a generic learning module.
|
||||
Initialized on access with __init__, first time with state=None, and
|
||||
then with state
|
||||
'''
|
||||
id_attribute = 'youtube'
|
||||
video_time = 0
|
||||
|
||||
@@ -31,12 +24,8 @@ class VideoModule(XModule):
|
||||
|
||||
def get_xml_tags():
|
||||
''' Tags in the courseware file guaranteed to correspond to the module '''
|
||||
return "video1"
|
||||
return "video"
|
||||
|
||||
def get_id_attribute():
|
||||
''' An attribute in the XML scheme that is guaranteed unique. '''
|
||||
return "youtube"
|
||||
|
||||
def get_html(self):
|
||||
return render_to_string('video.html',{'id':self.item_id,
|
||||
'time':self.video_time})
|
||||
|
||||
@@ -29,6 +29,8 @@ import content_parser
|
||||
|
||||
import uuid
|
||||
|
||||
from module_render import *
|
||||
|
||||
template_imports={'urllib':urllib}
|
||||
|
||||
def profile(request):
|
||||
@@ -98,131 +100,6 @@ def render_accordion(request,course,chapter,section):
|
||||
return {'init_js':render_to_string('accordion_init.js',context),
|
||||
'content':render_to_string('accordion.html',context)}
|
||||
|
||||
def html_module(request, module):
|
||||
''' Show basic text
|
||||
'''
|
||||
template_source=module.getAttribute('filename')
|
||||
return {'content':render_to_string(template_source, {})}
|
||||
|
||||
def vertical_module(request, module):
|
||||
''' Layout module which lays out content vertically.
|
||||
'''
|
||||
contents=[(e.getAttribute("name"),render_module(request, e)) \
|
||||
for e in module.childNodes \
|
||||
if e.nodeType==1]
|
||||
js="".join([e[1]['init_js'] for e in contents if 'init_js' in e[1]])
|
||||
|
||||
return {'init_js':js,
|
||||
'content':render_to_string('vert_module.html',{'items':contents})}
|
||||
|
||||
def seq_module(request, module):
|
||||
''' Layout module which lays out content in a temporal sequence
|
||||
'''
|
||||
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
|
||||
if 'init_js' not in m: m['init_js']=""
|
||||
content=json.dumps(m['content'])
|
||||
content=content.replace('</script>', '<"+"/script>')
|
||||
return {'content':content, 'init_js':m['init_js']}
|
||||
contents=[(e.getAttribute("name"),j(render_module(request, e))) \
|
||||
for e in module.childNodes \
|
||||
if e.nodeType==1]
|
||||
|
||||
js="".join([e[1]['init_js'] for e in contents if 'init_js' in e[1]])
|
||||
|
||||
iid=uuid.uuid1().hex
|
||||
|
||||
params={'items':contents,
|
||||
'id':"seq"}
|
||||
|
||||
print module.nodeName
|
||||
if module.nodeName == 'sequential':
|
||||
return {'init_js':js+render_to_string('seq_module.js',params),
|
||||
'content':render_to_string('seq_module.html',params)}
|
||||
if module.nodeName == 'tab':
|
||||
return {'init_js':js+render_to_string('tab_module.js',params),
|
||||
'content':render_to_string('tab_module.html',params)}
|
||||
|
||||
|
||||
modx_modules={'problem':capa_module.LoncapaModule, 'video':video_module.VideoModule}
|
||||
|
||||
def render_x_module(request, xml_module):
|
||||
''' Generic module for extensions. This renders to HTML. '''
|
||||
# Check if problem has an instance in DB
|
||||
module_type=xml_module.nodeName
|
||||
module_class=modx_modules[module_type]
|
||||
module_id=xml_module.getAttribute(module_class.id_attribute)
|
||||
|
||||
# Grab state from database
|
||||
s = StudentModule.objects.filter(student=request.user,
|
||||
module_id=module_id,
|
||||
module_type = module_type)
|
||||
if len(s) == 0: # If nothing in the database...
|
||||
state=None
|
||||
else:
|
||||
smod = s[0]
|
||||
state = smod.state
|
||||
|
||||
# Create a new instance
|
||||
ajax_url = '/modx/'+module_type+'/'+module_id+'/'
|
||||
instance=module_class(xml_module.toxml(),
|
||||
module_id,
|
||||
ajax_url=ajax_url,
|
||||
state=state)
|
||||
|
||||
# If instance wasn't already in the database, create it
|
||||
if len(s) == 0:
|
||||
smod=StudentModule(student=request.user,
|
||||
module_type = module_type,
|
||||
module_id=module_id,
|
||||
state=instance.get_state(),
|
||||
xml=instance.xml)
|
||||
# Grab content
|
||||
content = {'content':instance.get_html(),
|
||||
'init_js':instance.get_init_js()}
|
||||
|
||||
smod.save() # This may be optional (at least in the case of no instance in the dB)
|
||||
|
||||
return content
|
||||
|
||||
def modx_dispatch(request, module=None, dispatch=None, id=None):
|
||||
''' Generic module for extensions. '''
|
||||
s = StudentModule.objects.filter(module_type=module, student=request.user, module_id=id)
|
||||
if len(s) == 0:
|
||||
raise Http404
|
||||
|
||||
s=s[0]
|
||||
|
||||
dispatch=dispatch.split('?')[0]
|
||||
|
||||
ajax_url = '/modx/'+module+'/'+id+'/'
|
||||
|
||||
instance=modx_modules[module](s.xml, s.module_id, ajax_url=ajax_url, state=s.state)
|
||||
html=instance.handle_ajax(dispatch, request.GET)
|
||||
s.state=instance.get_state()
|
||||
s.grade=instance.get_score()['score']
|
||||
s.save()
|
||||
return HttpResponse(html)
|
||||
|
||||
module_types={'video':render_x_module,
|
||||
'html':html_module,
|
||||
'tab':seq_module,
|
||||
'vertical':vertical_module,
|
||||
'sequential':seq_module,
|
||||
'problem':render_x_module,
|
||||
}
|
||||
#'lab':lab_module,
|
||||
|
||||
def render_module(request, module):
|
||||
''' Generic dispatch for internal modules. '''
|
||||
if module==None:
|
||||
return {"content":""}
|
||||
if str(module.localName) in module_types:
|
||||
return module_types[module.localName](request, module)
|
||||
return {"content":""}
|
||||
|
||||
def index(request, course="6.002 Spring 2012", chapter="Using the System", section="Hints"):
|
||||
''' Displays courseware accordion, and any associated content.
|
||||
'''
|
||||
|
||||
@@ -3,14 +3,12 @@ class XModule:
|
||||
Initialized on access with __init__, first time with state=None, and
|
||||
then with state
|
||||
'''
|
||||
id_attribute='name' # An attribute guaranteed to be unique
|
||||
|
||||
def get_xml_tags():
|
||||
''' Tags in the courseware file guaranteed to correspond to the module '''
|
||||
return []
|
||||
|
||||
def get_id_attribute():
|
||||
''' An attribute in the XML scheme that is guaranteed unique. '''
|
||||
return "name"
|
||||
|
||||
def get_state(self):
|
||||
return ""
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
from djangomako.shortcuts import render_to_response, render_to_string
|
||||
from django.shortcuts import redirect
|
||||
|
||||
valid_templates=['index.html', 'staff.html', 'info.html']
|
||||
valid_templates=['index.html', 'staff.html', 'info.html', 'credits.html']
|
||||
|
||||
def index(request, template):
|
||||
if template in valid_templates:
|
||||
|
||||
Reference in New Issue
Block a user