Commit ed3f0817 by Calen Pennington

Merge branch 'master' into asset-pipeline

parents fe4f7bb2 4fe22be5
...@@ -25,6 +25,7 @@ from mako.template import Template ...@@ -25,6 +25,7 @@ from mako.template import Template
from util import contextualize_text from util import contextualize_text
import inputtypes import inputtypes
from responsetypes import NumericalResponse, FormulaResponse, CustomResponse, SchematicResponse, MultipleChoiceResponse, StudentInputError, TrueFalseResponse, ExternalResponse,ImageResponse,OptionResponse from responsetypes import NumericalResponse, FormulaResponse, CustomResponse, SchematicResponse, MultipleChoiceResponse, StudentInputError, TrueFalseResponse, ExternalResponse,ImageResponse,OptionResponse
import calc import calc
...@@ -166,7 +167,7 @@ class LoncapaProblem(object): ...@@ -166,7 +167,7 @@ class LoncapaProblem(object):
problems_simple = self.extract_problems(self.tree) problems_simple = self.extract_problems(self.tree)
for response in problems_simple: for response in problems_simple:
grader = response_types[response.tag](response, self.context, self.system) grader = response_types[response.tag](response, self.context, self.system)
results = grader.grade(answers) # call the responsetype instance to do the actual grading results = grader.get_score(answers) # call the responsetype instance to do the actual grading
self.correct_map.update(results) self.correct_map.update(results)
return self.correct_map return self.correct_map
...@@ -239,7 +240,7 @@ class LoncapaProblem(object): ...@@ -239,7 +240,7 @@ class LoncapaProblem(object):
# used to be # used to be
# if problemtree.tag in html_special_response: # if problemtree.tag in html_special_response:
if hasattr(inputtypes, problemtree.tag): if problemtree.tag in inputtypes.get_input_xml_tags():
# status is currently the answer for the problem ID for the input element, # status is currently the answer for the problem ID for the input element,
# but it will turn into a dict containing both the answer and any associated message # but it will turn into a dict containing both the answer and any associated message
# for the problem ID for the input element. # for the problem ID for the input element.
...@@ -266,9 +267,17 @@ class LoncapaProblem(object): ...@@ -266,9 +267,17 @@ class LoncapaProblem(object):
# print "[courseware.capa.capa_problem.extract_html] msg = ",msg # print "[courseware.capa.capa_problem.extract_html] msg = ",msg
# do the rendering # do the rendering
#render_function = html_special_response[problemtree.tag] # This should be broken out into a helper function
render_function = getattr(inputtypes, problemtree.tag) # that handles all input objects
return render_function(problemtree, value, status, msg) # render the special response (textline, schematic,...) render_object = inputtypes.SimpleInput(system = self.system,
xml = problemtree,
state = {'value':value,
'status': status,
'id':problemtree.get('id'),
'feedback':{'message':msg}
},
use = 'capa_input')
return render_object.get_html() #function(problemtree, value, status, msg) # render the special response (textline, schematic,...)
tree=Element(problemtree.tag) tree=Element(problemtree.tag)
for item in problemtree: for item in problemtree:
......
...@@ -32,8 +32,121 @@ from lxml import etree ...@@ -32,8 +32,121 @@ from lxml import etree
from mitxmako.shortcuts import render_to_string from mitxmako.shortcuts import render_to_string
def get_input_xml_tags():
''' Eventually, this will be for all registered input types '''
return SimpleInput.get_xml_tags()
class SimpleInput():# XModule
''' Type for simple inputs -- plain HTML with a form element
State is a dictionary with optional keys:
* Value
* ID
* Status (answered, unanswered, unsubmitted)
* Feedback (dictionary containing keys for hints, errors, or other
feedback from previous attempt)
'''
xml_tags = {} ## Maps tags to functions
@classmethod
def get_xml_tags(c):
return c.xml_tags.keys()
@classmethod
def get_uses(c):
return ['capa_input', 'capa_transform']
def get_html(self):
return self.xml_tags[self.tag](self.xml, self.value, self.status, self.msg)
def __init__(self, system, xml, item_id = None, track_url=None, state=None, use = 'capa_input'):
self.xml = xml
self.tag = xml.tag
if not state:
state = {}
## ID should only come from one place.
## If it comes from multiple, we use state first, XML second, and parameter
## third. Since we don't make this guarantee, we can swap this around in
## the future if there's a more logical order.
if item_id:
self.id = item_id
if xml.get('id'):
self.id = xml.get('id')
if 'id' in state:
self.id = state['id']
self.system = system
self.value = ''
if 'value' in state:
self.value = state['value']
self.msg = ''
if 'feedback' in state and 'message' in state['feedback']:
self.msg = state['feedback']['message']
self.status = 'unanswered'
if 'status' in state:
self.status = state['status']
## TODO
# class SimpleTransform():
# ''' Type for simple XML to HTML transforms. Examples:
# * Math tags, which go from LON-CAPA-style m-tags to MathJAX
# '''
# xml_tags = {} ## Maps tags to functions
# @classmethod
# def get_xml_tags(c):
# return c.xml_tags.keys()
# @classmethod
# def get_uses(c):
# return ['capa_transform']
# def get_html(self):
# return self.xml_tags[self.tag](self.xml, self.value, self.status, self.msg)
# def __init__(self, system, xml, item_id = None, track_url=None, state=None, use = 'capa_input'):
# self.xml = xml
# self.tag = xml.tag
# if not state:
# state = {}
# if item_id:
# self.id = item_id
# if xml.get('id'):
# self.id = xml.get('id')
# if 'id' in state:
# self.id = state['id']
# self.system = system
# self.value = ''
# if 'value' in state:
# self.value = state['value']
# self.msg = ''
# if 'feedback' in state and 'message' in state['feedback']:
# self.msg = state['feedback']['message']
# self.status = 'unanswered'
# if 'status' in state:
# self.status = state['status']
def register_render_function(fn, names=None, cls=SimpleInput):
if names == None:
SimpleInput.xml_tags[fn.__name__] = fn
else:
raise NotImplementedError
def wrapped():
return fn
return wrapped
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@register_render_function
def optioninput(element, value, status, msg=''): def optioninput(element, value, status, msg=''):
''' '''
Select option input type. Select option input type.
...@@ -67,7 +180,7 @@ def optioninput(element, value, status, msg=''): ...@@ -67,7 +180,7 @@ def optioninput(element, value, status, msg=''):
return etree.XML(html) return etree.XML(html)
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@register_render_function
def choicegroup(element, value, status, msg=''): def choicegroup(element, value, status, msg=''):
''' '''
Radio button inputs: multiple choice or true/false Radio button inputs: multiple choice or true/false
...@@ -90,6 +203,7 @@ def choicegroup(element, value, status, msg=''): ...@@ -90,6 +203,7 @@ def choicegroup(element, value, status, msg=''):
html=render_to_string("choicegroup.html", context) html=render_to_string("choicegroup.html", context)
return etree.XML(html) return etree.XML(html)
@register_render_function
def textline(element, value, state, msg=""): def textline(element, value, state, msg=""):
eid=element.get('id') eid=element.get('id')
count = int(eid.split('_')[-2])-1 # HACK count = int(eid.split('_')[-2])-1 # HACK
...@@ -100,6 +214,7 @@ def textline(element, value, state, msg=""): ...@@ -100,6 +214,7 @@ def textline(element, value, state, msg=""):
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@register_render_function
def js_textline(element, value, status, msg=''): def js_textline(element, value, status, msg=''):
''' '''
Plan: We will inspect element to figure out type Plan: We will inspect element to figure out type
...@@ -125,6 +240,7 @@ def js_textline(element, value, status, msg=''): ...@@ -125,6 +240,7 @@ def js_textline(element, value, status, msg=''):
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
## TODO: Make a wrapper for <codeinput> ## TODO: Make a wrapper for <codeinput>
@register_render_function
def textbox(element, value, status, msg=''): def textbox(element, value, status, msg=''):
''' '''
The textbox is used for code input. The message is the return HTML string from The textbox is used for code input. The message is the return HTML string from
...@@ -140,6 +256,7 @@ def textbox(element, value, status, msg=''): ...@@ -140,6 +256,7 @@ def textbox(element, value, status, msg=''):
return etree.XML(html) return etree.XML(html)
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@register_render_function
def schematic(element, value, status, msg=''): def schematic(element, value, status, msg=''):
eid = element.get('id') eid = element.get('id')
height = element.get('height') height = element.get('height')
...@@ -164,6 +281,7 @@ def schematic(element, value, status, msg=''): ...@@ -164,6 +281,7 @@ def schematic(element, value, status, msg=''):
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
### TODO: Move out of inputtypes ### TODO: Move out of inputtypes
@register_render_function
def math(element, value, status, msg=''): def math(element, value, status, msg=''):
''' '''
This is not really an input type. It is a convention from Lon-CAPA, used for This is not really an input type. It is a convention from Lon-CAPA, used for
...@@ -198,6 +316,7 @@ def math(element, value, status, msg=''): ...@@ -198,6 +316,7 @@ def math(element, value, status, msg=''):
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@register_render_function
def solution(element, value, status, msg=''): def solution(element, value, status, msg=''):
''' '''
This is not really an input type. It is just a <span>...</span> which is given an ID, This is not really an input type. It is just a <span>...</span> which is given an ID,
...@@ -218,6 +337,7 @@ def solution(element, value, status, msg=''): ...@@ -218,6 +337,7 @@ def solution(element, value, status, msg=''):
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@register_render_function
def imageinput(element, value, status, msg=''): def imageinput(element, value, status, msg=''):
''' '''
Clickable image as an input field. Element should specify the image source, height, and width, eg Clickable image as an input field. Element should specify the image source, height, and width, eg
...@@ -253,4 +373,3 @@ def imageinput(element, value, status, msg=''): ...@@ -253,4 +373,3 @@ def imageinput(element, value, status, msg=''):
print '[courseware.capa.inputtypes.imageinput] context=',context print '[courseware.capa.inputtypes.imageinput] context=',context
html=render_to_string("imageinput.html", context) html=render_to_string("imageinput.html", context)
return etree.XML(html) return etree.XML(html)
...@@ -47,10 +47,10 @@ def compare_with_tolerance(v1, v2, tol): ...@@ -47,10 +47,10 @@ def compare_with_tolerance(v1, v2, tol):
return abs(v1-v2) <= tolerance return abs(v1-v2) <= tolerance
class GenericResponse(object): class GenericResponse(object):
__metaclass__=abc.ABCMeta __metaclass__=abc.ABCMeta # abc = Abstract Base Class
@abc.abstractmethod @abc.abstractmethod
def grade(self, student_answers): def get_score(self, student_answers):
pass pass
@abc.abstractmethod @abc.abstractmethod
...@@ -61,7 +61,7 @@ class GenericResponse(object): ...@@ -61,7 +61,7 @@ class GenericResponse(object):
def preprocess_response(self): def preprocess_response(self):
pass pass
#Every response type needs methods "grade" and "get_answers" #Every response type needs methods "get_score" and "get_answers"
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
...@@ -95,7 +95,7 @@ class MultipleChoiceResponse(GenericResponse): ...@@ -95,7 +95,7 @@ class MultipleChoiceResponse(GenericResponse):
raise Exception("should have exactly one choice group per multiplechoicceresponse") raise Exception("should have exactly one choice group per multiplechoicceresponse")
self.answer_id=self.answer_id[0] self.answer_id=self.answer_id[0]
def grade(self, student_answers): def get_score(self, student_answers):
if self.answer_id in student_answers and student_answers[self.answer_id] in self.correct_choices: if self.answer_id in student_answers and student_answers[self.answer_id] in self.correct_choices:
return {self.answer_id:'correct'} return {self.answer_id:'correct'}
else: else:
...@@ -132,7 +132,7 @@ class TrueFalseResponse(MultipleChoiceResponse): ...@@ -132,7 +132,7 @@ class TrueFalseResponse(MultipleChoiceResponse):
else: else:
choice.set("name", "choice_"+choice.get("name")) choice.set("name", "choice_"+choice.get("name"))
def grade(self, student_answers): def get_score(self, student_answers):
correct = set(self.correct_choices) correct = set(self.correct_choices)
answers = set(student_answers.get(self.answer_id, [])) answers = set(student_answers.get(self.answer_id, []))
...@@ -162,7 +162,7 @@ class OptionResponse(GenericResponse): ...@@ -162,7 +162,7 @@ class OptionResponse(GenericResponse):
print '[courseware.capa.responsetypes.OR.init] answer_fields=%s' % (self.answer_fields) print '[courseware.capa.responsetypes.OR.init] answer_fields=%s' % (self.answer_fields)
self.context = context self.context = context
def grade(self, student_answers): def get_score(self, student_answers):
cmap = {} cmap = {}
amap = self.get_answers() amap = self.get_answers()
for aid in amap: for aid in amap:
...@@ -194,7 +194,7 @@ class NumericalResponse(GenericResponse): ...@@ -194,7 +194,7 @@ class NumericalResponse(GenericResponse):
except Exception, err: except Exception, err:
self.answer_id = None self.answer_id = None
def grade(self, student_answers): def get_score(self, student_answers):
''' Display HTML for a numeric response ''' ''' Display HTML for a numeric response '''
student_answer = student_answers[self.answer_id] student_answer = student_answers[self.answer_id]
try: try:
...@@ -300,7 +300,7 @@ def sympy_check2(): ...@@ -300,7 +300,7 @@ def sympy_check2():
else: else:
self.code = answer.text self.code = answer.text
def grade(self, student_answers): def get_score(self, student_answers):
''' '''
student_answers is a dict with everything from request.POST, but with the first part student_answers is a dict with everything from request.POST, but with the first part
of each key removed (the string before the first "_"). of each key removed (the string before the first "_").
...@@ -363,7 +363,7 @@ def sympy_check2(): ...@@ -363,7 +363,7 @@ def sympy_check2():
print "oops in customresponse (cfn) error %s" % err print "oops in customresponse (cfn) error %s" % err
# print "context = ",self.context # print "context = ",self.context
print traceback.format_exc() print traceback.format_exc()
if settings.DEBUG: print "[courseware.capa.responsetypes.customresponse.grade] ret = ",ret if settings.DEBUG: print "[courseware.capa.responsetypes.customresponse.get_score] ret = ",ret
if type(ret)==dict: if type(ret)==dict:
correct[0] = 'correct' if ret['ok'] else 'incorrect' correct[0] = 'correct' if ret['ok'] else 'incorrect'
msg = ret['msg'] msg = ret['msg']
...@@ -428,7 +428,7 @@ class ExternalResponse(GenericResponse): ...@@ -428,7 +428,7 @@ class ExternalResponse(GenericResponse):
self.tests = xml.get('answer') self.tests = xml.get('answer')
def grade(self, student_answers): def get_score(self, student_answers):
submission = [student_answers[k] for k in sorted(self.answer_ids)] submission = [student_answers[k] for k in sorted(self.answer_ids)]
self.context.update({'submission':submission}) self.context.update({'submission':submission})
...@@ -504,7 +504,7 @@ class FormulaResponse(GenericResponse): ...@@ -504,7 +504,7 @@ class FormulaResponse(GenericResponse):
self.case_sensitive = False self.case_sensitive = False
def grade(self, student_answers): def get_score(self, student_answers):
variables=self.samples.split('@')[0].split(',') variables=self.samples.split('@')[0].split(',')
numsamples=int(self.samples.split('@')[1].split('#')[1]) numsamples=int(self.samples.split('@')[1].split('#')[1])
sranges=zip(*map(lambda x:map(float, x.split(",")), sranges=zip(*map(lambda x:map(float, x.split(",")),
...@@ -566,7 +566,7 @@ class SchematicResponse(GenericResponse): ...@@ -566,7 +566,7 @@ class SchematicResponse(GenericResponse):
else: else:
self.code = answer.text self.code = answer.text
def grade(self, student_answers): def get_score(self, student_answers):
from capa_problem import global_context from capa_problem import global_context
submission = [json.loads(student_answers[k]) for k in sorted(self.answer_ids)] submission = [json.loads(student_answers[k]) for k in sorted(self.answer_ids)]
self.context.update({'submission':submission}) self.context.update({'submission':submission})
...@@ -605,7 +605,7 @@ class ImageResponse(GenericResponse): ...@@ -605,7 +605,7 @@ class ImageResponse(GenericResponse):
self.ielements = xml.findall('imageinput') self.ielements = xml.findall('imageinput')
self.answer_ids = [ie.get('id') for ie in self.ielements] self.answer_ids = [ie.get('id') for ie in self.ielements]
def grade(self, student_answers): def get_score(self, student_answers):
correct_map = {} correct_map = {}
expectedset = self.get_answers() expectedset = self.get_answers()
......
def contextualize_text(text, context): # private def contextualize_text(text, context): # private
''' Takes a string with variables. E.g. $a+$b. ''' Takes a string with variables. E.g. $a+$b.
Does a substitution of those variables from the context ''' Does a substitution of those variables from the context '''
if not text: return text
for key in sorted(context, lambda x,y:cmp(len(y),len(x))): for key in sorted(context, lambda x,y:cmp(len(y),len(x))):
text=text.replace('$'+key, str(context[key])) text=text.replace('$'+key, str(context[key]))
return text return text
...@@ -13,8 +13,8 @@ from fs.osfs import OSFS ...@@ -13,8 +13,8 @@ from fs.osfs import OSFS
from django.conf import settings from django.conf import settings
from mitxmako.shortcuts import render_to_string from mitxmako.shortcuts import render_to_string
from models import StudentModule from models import StudentModule
from multicourse import multicourse_settings
import courseware.modules import courseware.modules
...@@ -31,6 +31,8 @@ class I4xSystem(object): ...@@ -31,6 +31,8 @@ class I4xSystem(object):
self.track_function = track_function self.track_function = track_function
if not filestore: if not filestore:
self.filestore = OSFS(settings.DATA_DIR) self.filestore = OSFS(settings.DATA_DIR)
else:
self.filestore = filestore
self.render_function = render_function self.render_function = render_function
self.exception404 = Http404 self.exception404 = Http404
def __repr__(self): def __repr__(self):
...@@ -95,15 +97,15 @@ def render_x_module(user, request, xml_module, module_object_preload): ...@@ -95,15 +97,15 @@ def render_x_module(user, request, xml_module, module_object_preload):
state = smod.state state = smod.state
# get coursename if stored # get coursename if stored
if 'coursename' in request.session: coursename = request.session['coursename'] coursename = multicourse_settings.get_coursename_from_request(request)
else: coursename = None xp = multicourse_settings.get_course_xmlpath(coursename) # path to XML for the course
# Create a new instance # Create a new instance
ajax_url = settings.MITX_ROOT_URL + '/modx/'+module_type+'/'+module_id+'/' ajax_url = settings.MITX_ROOT_URL + '/modx/'+module_type+'/'+module_id+'/'
system = I4xSystem(track_function = make_track_function(request), 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_module(user, request, x, module_object_preload),
ajax_url = ajax_url, ajax_url = ajax_url,
filestore = None filestore = OSFS(settings.DATA_DIR + xp),
) )
instance=module_class(system, instance=module_class(system,
etree.tostring(xml_module), etree.tostring(xml_module),
......
...@@ -2,6 +2,8 @@ import logging ...@@ -2,6 +2,8 @@ import logging
import urllib import urllib
import json import json
from fs.osfs import OSFS
from django.conf import settings from django.conf import settings
from django.core.context_processors import csrf from django.core.context_processors import csrf
from django.contrib.auth.models import User from django.contrib.auth.models import User
...@@ -38,9 +40,7 @@ def gradebook(request): ...@@ -38,9 +40,7 @@ def gradebook(request):
if 'course_admin' not in content_parser.user_groups(request.user): if 'course_admin' not in content_parser.user_groups(request.user):
raise Http404 raise Http404
# TODO: This should be abstracted out. We repeat this logic many times. coursename = multicourse_settings.get_coursename_from_request(request)
if 'coursename' in request.session: coursename = request.session['coursename']
else: coursename = None
student_objects = User.objects.all()[:100] student_objects = User.objects.all()[:100]
student_info = [{'username' :s.username, student_info = [{'username' :s.username,
...@@ -68,8 +68,7 @@ def profile(request, student_id = None): ...@@ -68,8 +68,7 @@ def profile(request, student_id = None):
user_info = UserProfile.objects.get(user=student) # request.user.profile_cache # user_info = UserProfile.objects.get(user=student) # request.user.profile_cache #
if 'coursename' in request.session: coursename = request.session['coursename'] coursename = multicourse_settings.get_coursename_from_request(request)
else: coursename = None
context={'name':user_info.name, context={'name':user_info.name,
'username':student.username, 'username':student.username,
...@@ -110,8 +109,7 @@ def render_section(request, section): ...@@ -110,8 +109,7 @@ def render_section(request, section):
if not settings.COURSEWARE_ENABLED: if not settings.COURSEWARE_ENABLED:
return redirect('/') return redirect('/')
if 'coursename' in request.session: coursename = request.session['coursename'] coursename = multicourse_settings.get_coursename_from_request(request)
else: coursename = None
try: try:
dom = content_parser.section_file(user, section, coursename) dom = content_parser.section_file(user, section, coursename)
...@@ -251,8 +249,8 @@ def modx_dispatch(request, module=None, dispatch=None, id=None): ...@@ -251,8 +249,8 @@ def modx_dispatch(request, module=None, dispatch=None, id=None):
ajax_url = settings.MITX_ROOT_URL + '/modx/'+module+'/'+id+'/' ajax_url = settings.MITX_ROOT_URL + '/modx/'+module+'/'+id+'/'
# get coursename if stored # get coursename if stored
if 'coursename' in request.session: coursename = request.session['coursename'] coursename = multicourse_settings.get_coursename_from_request(request)
else: coursename = None xp = multicourse_settings.get_course_xmlpath(coursename) # path to XML for the course
# Grab the XML corresponding to the request from course.xml # Grab the XML corresponding to the request from course.xml
try: try:
...@@ -269,7 +267,7 @@ def modx_dispatch(request, module=None, dispatch=None, id=None): ...@@ -269,7 +267,7 @@ def modx_dispatch(request, module=None, dispatch=None, id=None):
system = I4xSystem(track_function = make_track_function(request), system = I4xSystem(track_function = make_track_function(request),
render_function = None, render_function = None,
ajax_url = ajax_url, ajax_url = ajax_url,
filestore = None filestore = OSFS(settings.DATA_DIR + xp),
) )
try: try:
...@@ -307,12 +305,12 @@ def quickedit(request, id=None): ...@@ -307,12 +305,12 @@ def quickedit(request, id=None):
print "In deployed use, this will only edit on one server" print "In deployed use, this will only edit on one server"
print "We need a setting to disable for production where there is" print "We need a setting to disable for production where there is"
print "a load balanacer" print "a load balanacer"
if not request.user.is_staff(): if not request.user.is_staff:
return redirect('/') return redirect('/')
# get coursename if stored # get coursename if stored
if 'coursename' in request.session: coursename = request.session['coursename'] coursename = multicourse_settings.get_coursename_from_request(request)
else: coursename = None xp = multicourse_settings.get_course_xmlpath(coursename) # path to XML for the course
def get_lcp(coursename,id): def get_lcp(coursename,id):
# Grab the XML corresponding to the request from course.xml # Grab the XML corresponding to the request from course.xml
...@@ -325,9 +323,8 @@ def quickedit(request, id=None): ...@@ -325,9 +323,8 @@ def quickedit(request, id=None):
system = I4xSystem(track_function = make_track_function(request), system = I4xSystem(track_function = make_track_function(request),
render_function = None, render_function = None,
ajax_url = ajax_url, ajax_url = ajax_url,
filestore = None, filestore = OSFS(settings.DATA_DIR + xp),
coursename = coursename, #role = 'staff' if request.user.is_staff else 'student', # TODO: generalize this
role = 'staff' if request.user.is_staff else 'student', # TODO: generalize this
) )
instance=courseware.modules.get_module_class(module)(system, instance=courseware.modules.get_module_class(module)(system,
xml, xml,
......
...@@ -42,6 +42,11 @@ else: # default to 6.002_Spring_2012 ...@@ -42,6 +42,11 @@ else: # default to 6.002_Spring_2012
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# wrapper functions around course settings # wrapper functions around course settings
def get_coursename_from_request(request):
if 'coursename' in request.session: coursename = request.session['coursename']
else: coursename = None
return coursename
def get_course_settings(coursename): def get_course_settings(coursename):
if not coursename: if not coursename:
if hasattr(settings,'COURSE_DEFAULT'): if hasattr(settings,'COURSE_DEFAULT'):
......
# multicourse/views.py import datetime
import json
import sys
from django.conf import settings
from django.contrib.auth.models import User
from django.core.context_processors import csrf
from django.core.mail import send_mail
from django.http import Http404
from django.http import HttpResponse
from django.shortcuts import redirect
from mitxmako.shortcuts import render_to_response, render_to_string
import courseware.capa.calc
import track.views
from multicourse import multicourse_settings
def mitxhome(request):
''' Home page (link from main header). List of courses. '''
if settings.ENABLE_MULTICOURSE:
context = {'courseinfo' : multicourse_settings.COURSE_SETTINGS}
return render_to_response("mitxhome.html", context)
return info(request)
...@@ -61,12 +61,6 @@ def info(request): ...@@ -61,12 +61,6 @@ def info(request):
''' Info page (link from main header) ''' ''' Info page (link from main header) '''
return render_to_response("info.html", {}) return render_to_response("info.html", {})
def mitxhome(request):
''' Home page (link from main header). List of courses. '''
if settings.ENABLE_MULTICOURSE:
return render_to_response("mitxhome.html", {})
return info(request)
# From http://djangosnippets.org/snippets/1042/ # From http://djangosnippets.org/snippets/1042/
def parse_accept_header(accept): def parse_accept_header(accept):
"""Parse the Accept header *accept*, returning a list with pairs of """Parse the Accept header *accept*, returning a list with pairs of
......
##
## File: templates/mathjax_include.html
##
## Advanced mathjax using 2.0-latest CDN for Dynamic Math
##
## This enables ASCIIMathJAX, and is used by js_textbox
<script type="text/x-mathjax-config">
// (function () {
var QUEUE = MathJax.Hub.queue; // shorthand for the queue
var math = null;
var jaxset = {}; // associative array of the element jaxs for the math output.
var mmlset = {}; // associative array of mathml from each jax
// constructs mathML of the specified jax element
function toMathML(jax,callback) {
var mml;
try {
mml = jax.root.toMathML("");
} catch(err) {
if (!err.restart) {throw err} // an actual error
return MathJax.Callback.After([toMathML,jax,callback],err.restart);
}
MathJax.Callback(callback)(mml);
}
// function to queue in MathJax to get put the MathML expression in in the right document element
function UpdateMathML(jax,id) {
toMathML(jax,function (mml) {
// document.getElementById(id+'_fromjs').value=math.originalText+ "\n\n=>\n\n"+ mml;
delem = document.getElementById("input_" + id + "_fromjs");
if (delem) { delem.value=mml; };
mmlset[id] = mml;
})
}
MathJax.Hub.Config({
tex2jax: {
inlineMath: [
["\\(","\\)"],
['[mathjaxinline]','[/mathjaxinline]']
],
displayMath: [
["\\[","\\]"],
['[mathjax]','[/mathjax]']
]
}
});
//
// The onchange event handler that typesets the
// math entered by the user
//
window.UpdateMath = function (Am,id) {
QUEUE.Push(["Text",jaxset[id],Am]);
QUEUE.Push(UpdateMathML(jaxset[id],id));
}
// })();
function DoUpdateMath(inputId) {
var str = document.getElementById("input_"+inputId).value;
// make sure the input field is in the jaxset
if ($.inArray(inputId,jaxset) == -1){
//alert('missing '+inputId);
if (document.getElementById("display_" + inputId)){
MathJax.Hub.queue.Push(function () {
math = MathJax.Hub.getAllJax("display_" + inputId)[0];
if (math){
jaxset[inputId] = math;
}
});
};
}
UpdateMath(str,inputId)
}
</script>
<%block name="headextra"/>
<!-- This must appear after all mathjax-config blocks, so it is after the imports from the other templates -->
<!-- TODO: move to settings -->
<script type="text/javascript"
src="http://cdn.mathjax.org/mathjax/2.0-latest/MathJax.js?config=TeX-MML-AM_HTMLorMML-full">
</script>
...@@ -28,10 +28,10 @@ $(document).ready(function(){ ...@@ -28,10 +28,10 @@ $(document).ready(function(){
<hr width="100%"> <hr width="100%">
<h3>Courses available:</h3> <h3>Courses available:</h3>
<ul> <ul>
<li><a href=${ MITX_ROOT_URL }/courseware/6.002_Spring_2012/>6.002 (Spring 2012)</a></li> % for coursename, info in courseinfo.items():
<li><a href=${ MITX_ROOT_URL }/courseware/8.02_Spring_2013/>8.02 (Spring 2013)</a></li> <li><a href=${ MITX_ROOT_URL }/courseware/${coursename}/>${info['title']} (${coursename})</a></li>
<li><a href=${ MITX_ROOT_URL }/courseware/8.01_Spring_2013/>8.01 (Spring 201x)</a></li> % endfor
</ul> </ul>
</section> </section>
</div> </div>
</section> </section>
<form class="option-input">
<select name="input_${id}" id="input_${id}" >
<option value="option_${id}_dummy_default"> </option>
% for option_id, option_description in options.items():
<option value="${option_id}"
% if (option_id==value):
selected="true"
% endif
> ${option_description}</option>
% endfor
</select>
<span id="answer_${id}"></span>
% if state == 'unsubmitted':
<span class="unanswered" style="display:inline-block;" id="status_${id}"></span>
% elif state == 'correct':
<span class="correct" id="status_${id}"></span>
% elif state == 'incorrect':
<span class="incorrect" id="status_${id}"></span>
% elif state == 'incomplete':
<span class="incorrect" id="status_${id}"></span>
% endif
</form>
...@@ -3,6 +3,15 @@ ...@@ -3,6 +3,15 @@
% if problem['weight']: % if problem['weight']:
: ${ problem['weight'] } points : ${ problem['weight'] } points
% endif % endif
% if settings.QUICKEDIT:
<span class="staff">
<br/>
<br/>
<br/>
<br/>
<font size=-2><a href=${MITX_ROOT_URL}/quickedit/${id}>Quick
Edit Problem</a></font></span>
% endif
</h2> </h2>
<section class="problem"> <section class="problem">
......
...@@ -70,7 +70,7 @@ if settings.COURSEWARE_ENABLED: ...@@ -70,7 +70,7 @@ if settings.COURSEWARE_ENABLED:
) )
if settings.ENABLE_MULTICOURSE: if settings.ENABLE_MULTICOURSE:
urlpatterns += (url(r'^mitxhome$', 'util.views.mitxhome'),) urlpatterns += (url(r'^mitxhome$', 'multicourse.views.mitxhome'),)
if settings.QUICKEDIT: if settings.QUICKEDIT:
urlpatterns += (url(r'^quickedit/(?P<id>[^/]*)$', 'courseware.views.quickedit'),) urlpatterns += (url(r'^quickedit/(?P<id>[^/]*)$', 'courseware.views.quickedit'),)
......
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