Merge
This commit is contained in:
@@ -74,8 +74,10 @@ def login_user(request, error=""):
|
||||
log.critical("Login failed - Could not create session. Is memcached running?")
|
||||
log.exception(e)
|
||||
|
||||
log.info("Login success - {0} ({1})".format(username, email))
|
||||
return HttpResponse(json.dumps({'success':True}))
|
||||
|
||||
log.warning("Login failed - Account not active for user {0}".format(username))
|
||||
return HttpResponse(json.dumps({'success':False,
|
||||
'error': 'Account not active. Check your e-mail.'}))
|
||||
|
||||
|
||||
@@ -133,7 +133,7 @@ class LoncapaProblem(object):
|
||||
for entry in problems_simple.xpath("//"+"|//".join(response_properties+entry_types)):
|
||||
answer = entry.get('correct_answer')
|
||||
if answer != None:
|
||||
answer_map[entry.get('id')] = contextualize_text(answer, self.context())
|
||||
answer_map[entry.get('id')] = contextualize_text(answer, self.context)
|
||||
|
||||
return answer_map
|
||||
|
||||
|
||||
@@ -3,12 +3,16 @@ from util import contextualize_text
|
||||
from calc import evaluator
|
||||
import random, math
|
||||
from django.conf import settings
|
||||
import eia
|
||||
import calc
|
||||
|
||||
# TODO: Should be the same object as in capa_problem
|
||||
global_context={'random':random,
|
||||
'numpy':numpy,
|
||||
'math':math,
|
||||
'scipy':scipy}
|
||||
'scipy':scipy,
|
||||
'calc':calc,
|
||||
'eia':eia}
|
||||
|
||||
class numericalresponse(object):
|
||||
def __init__(self, xml, context):
|
||||
|
||||
130
courseware/capa/unit.py
Normal file
130
courseware/capa/unit.py
Normal file
@@ -0,0 +1,130 @@
|
||||
import math
|
||||
from numpy import eye, array
|
||||
import operator
|
||||
from pyparsing import Word, alphas, nums, oneOf, Literal
|
||||
from pyparsing import ZeroOrMore, OneOrMore, StringStart
|
||||
from pyparsing import StringEnd, Optional, Forward
|
||||
from pyparsing import CaselessLiteral, Group, StringEnd
|
||||
from pyparsing import NoMatch, stringEnd
|
||||
|
||||
base_units = ['meter', 'gram', 'second', 'ampere', 'kelvin', 'mole', 'cd']
|
||||
unit_vectors = dict([(base_units[i], eye(len(base_units))[:,i]) for i in range(len(base_units))])
|
||||
|
||||
|
||||
def unit_evaluator(unit_string, units=unit_map):
|
||||
''' Evaluate an expression. Variables are passed as a dictionary
|
||||
from string to value. Unary functions are passed as a dictionary
|
||||
from string to function '''
|
||||
if string.strip() == "":
|
||||
return float('nan')
|
||||
ops = { "^" : operator.pow,
|
||||
"*" : operator.mul,
|
||||
"/" : operator.truediv,
|
||||
}
|
||||
prefixes={'%':0.01,'k':1e3,'M':1e6,'G':1e9,
|
||||
'T':1e12,#'P':1e15,'E':1e18,'Z':1e21,'Y':1e24,
|
||||
'c':1e-2,'m':1e-3,'u':1e-6,
|
||||
'n':1e-9,'p':1e-12}#,'f':1e-15,'a':1e-18,'z':1e-21,'y':1e-24}
|
||||
|
||||
def super_float(text):
|
||||
''' Like float, but with si extensions. 1k goes to 1000'''
|
||||
if text[-1] in suffixes:
|
||||
return float(text[:-1])*suffixes[text[-1]]
|
||||
else:
|
||||
return float(text)
|
||||
|
||||
def number_parse_action(x): # [ '7' ] -> [ 7 ]
|
||||
return [super_float("".join(x))]
|
||||
def exp_parse_action(x): # [ 2 ^ 3 ^ 2 ] -> 512
|
||||
x = [e for e in x if type(e) == float] # Ignore ^
|
||||
x.reverse()
|
||||
x=reduce(lambda a,b:b**a, x)
|
||||
return x
|
||||
def parallel(x): # Parallel resistors [ 1 2 ] => 2/3
|
||||
if len(x) == 1:
|
||||
return x[0]
|
||||
if 0 in x:
|
||||
return float('nan')
|
||||
x = [1./e for e in x if type(e) == float] # Ignore ^
|
||||
return 1./sum(x)
|
||||
def sum_parse_action(x): # [ 1 + 2 - 3 ] -> 0
|
||||
total = 0.0
|
||||
op = ops['+']
|
||||
for e in x:
|
||||
if e in set('+-'):
|
||||
op = ops[e]
|
||||
else:
|
||||
total=op(total, e)
|
||||
return total
|
||||
def prod_parse_action(x): # [ 1 * 2 / 3 ] => 0.66
|
||||
prod = 1.0
|
||||
op = ops['*']
|
||||
for e in x:
|
||||
if e in set('*/'):
|
||||
op = ops[e]
|
||||
else:
|
||||
prod=op(prod, e)
|
||||
return prod
|
||||
def func_parse_action(x):
|
||||
return [functions[x[0]](x[1])]
|
||||
|
||||
number_suffix=reduce(lambda a,b:a|b, map(Literal,suffixes.keys()), NoMatch()) # SI suffixes and percent
|
||||
(dot,minus,plus,times,div,lpar,rpar,exp)=map(Literal,".-+*/()^")
|
||||
|
||||
number_part=Word(nums)
|
||||
inner_number = ( number_part+Optional("."+number_part) ) | ("."+number_part) # 0.33 or 7 or .34
|
||||
number=Optional(minus | plus)+ inner_number + \
|
||||
Optional(CaselessLiteral("E")+Optional("-")+number_part)+ \
|
||||
Optional(number_suffix) # 0.33k or -17
|
||||
number=number.setParseAction( number_parse_action ) # Convert to number
|
||||
|
||||
# Predefine recursive variables
|
||||
expr = Forward()
|
||||
factor = Forward()
|
||||
|
||||
def sreduce(f, l):
|
||||
''' Same as reduce, but handle len 1 and len 0 lists sensibly '''
|
||||
if len(l)==0:
|
||||
return NoMatch()
|
||||
if len(l)==1:
|
||||
return l[0]
|
||||
return reduce(f, l)
|
||||
|
||||
# Handle variables passed in. E.g. if we have {'R':0.5}, we make the substitution.
|
||||
# Special case for no variables because of how we understand PyParsing is put together
|
||||
if len(variables)>0:
|
||||
varnames = sreduce(lambda x,y:x|y, map(lambda x: CaselessLiteral(x), variables.keys()))
|
||||
varnames.setParseAction(lambda x:map(lambda y:variables[y], x))
|
||||
else:
|
||||
varnames=NoMatch()
|
||||
# Same thing for functions.
|
||||
if len(functions)>0:
|
||||
funcnames = sreduce(lambda x,y:x|y, map(lambda x: CaselessLiteral(x), functions.keys()))
|
||||
function = funcnames+lpar.suppress()+expr+rpar.suppress()
|
||||
function.setParseAction(func_parse_action)
|
||||
else:
|
||||
function = NoMatch()
|
||||
|
||||
atom = number | varnames | lpar+expr+rpar | function
|
||||
factor << (atom + ZeroOrMore(exp+atom)).setParseAction(exp_parse_action) # 7^6
|
||||
paritem = factor + ZeroOrMore(Literal('||')+factor) # 5k || 4k
|
||||
paritem=paritem.setParseAction(parallel)
|
||||
term = paritem + ZeroOrMore((times|div)+paritem) # 7 * 5 / 4 - 3
|
||||
term = term.setParseAction(prod_parse_action)
|
||||
expr << Optional((plus|minus)) + term + ZeroOrMore((plus|minus)+term) # -5 + 4 - 3
|
||||
expr=expr.setParseAction(sum_parse_action)
|
||||
return (expr+stringEnd).parseString(string)[0]
|
||||
|
||||
if __name__=='__main__':
|
||||
variables={'R1':2.0, 'R3':4.0}
|
||||
functions={'sin':math.sin, 'cos':math.cos}
|
||||
print "X",evaluator(variables, functions, "10000||sin(7+5)-6k")
|
||||
print "X",evaluator(variables, functions, "13")
|
||||
print evaluator({'R1': 2.0, 'R3':4.0}, {}, "13")
|
||||
#
|
||||
print evaluator({'a': 2.2997471478310274, 'k': 9, 'm': 8, 'x': 0.66009498411213041}, {}, "5")
|
||||
print evaluator({},{}, "-1")
|
||||
print evaluator({},{}, "-(7+5)")
|
||||
print evaluator({},{}, "-0.33")
|
||||
print evaluator({},{}, "-.33")
|
||||
print evaluator({},{}, "5+7 QWSEKO")
|
||||
@@ -1,36 +1,39 @@
|
||||
from x_module import XModule
|
||||
from lxml import etree
|
||||
|
||||
import logging
|
||||
import json
|
||||
|
||||
## TODO: Abstract out from Django
|
||||
from django.conf import settings
|
||||
from djangomako.shortcuts import render_to_response, render_to_string
|
||||
from lxml import etree
|
||||
|
||||
from x_module import XModule
|
||||
|
||||
log = logging.getLogger("mitx.courseware.modules")
|
||||
|
||||
class VideoModule(XModule):
|
||||
#id_attribute = 'youtube'
|
||||
video_time = 0
|
||||
|
||||
def handle_ajax(self, dispatch, get):
|
||||
print "GET", get
|
||||
print "DISPATCH", dispatch
|
||||
if dispatch=='goto_position':
|
||||
log.debug(u"GET {0}".format(get))
|
||||
log.debug(u"DISPATCH {0}".format(dispatch))
|
||||
if dispatch == 'goto_position':
|
||||
self.position = int(float(get['position']))
|
||||
print "NEW POSITION", self.position
|
||||
log.debug(u"NEW POSITION {0}".format(self.position))
|
||||
return json.dumps({'success':True})
|
||||
raise Http404()
|
||||
|
||||
def get_state(self):
|
||||
print "STATE POSITION", self.position
|
||||
log.debug(u"STATE POSITION {0}".format(self.position))
|
||||
return json.dumps({ 'position':self.position })
|
||||
|
||||
def get_xml_tags():
|
||||
''' Tags in the courseware file guaranteed to correspond to the module '''
|
||||
'''Tags in the courseware file guaranteed to correspond to the module'''
|
||||
return "video"
|
||||
|
||||
def video_list(self):
|
||||
l=self.youtube.split(',')
|
||||
l=[i.split(":") for i in l]
|
||||
l = self.youtube.split(',')
|
||||
l = [i.split(":") for i in l]
|
||||
return json.dumps(dict(l))
|
||||
|
||||
def get_html(self):
|
||||
@@ -39,24 +42,25 @@ class VideoModule(XModule):
|
||||
'position':self.position})
|
||||
|
||||
def get_init_js(self):
|
||||
''' JavaScript code to be run when problem is shown. Be aware
|
||||
'''JavaScript code to be run when problem is shown. Be aware
|
||||
that this may happen several times on the same page
|
||||
(e.g. student switching tabs). Common functions should be put
|
||||
in the main course .js files for now. '''
|
||||
print "INIT POSITION", self.position
|
||||
log.debug(u"INIT POSITION {0}".format(self.position))
|
||||
return render_to_string('video_init.js',{'streams':self.video_list(),
|
||||
'id':self.item_id,
|
||||
'position':self.position})
|
||||
|
||||
def get_destroy_js(self):
|
||||
return "videoDestroy(\""+self.item_id+"\");"
|
||||
return "videoDestroy(\"{0}\");".format(self.item_id)
|
||||
|
||||
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)
|
||||
self.youtube = etree.XML(xml).get('youtube')
|
||||
self.position = 0
|
||||
if state!=None:
|
||||
if state != None:
|
||||
state = json.loads(state)
|
||||
if 'position' in state: self.position = int(float(state['position']))
|
||||
print "POOSITION IN STATE"
|
||||
print "LOAD POSITION", self.position
|
||||
if 'position' in state:
|
||||
self.position = int(float(state['position']))
|
||||
log.debug("POSITION IN STATE")
|
||||
log.debug(u"LOAD POSITION {0}".format(self.position))
|
||||
|
||||
@@ -155,7 +155,7 @@ LOGGING = {
|
||||
'stream' : sys.stderr,
|
||||
},
|
||||
'app' : {
|
||||
'level' : 'INFO',
|
||||
'level' : 'DEBUG' if DEBUG else 'INFO',
|
||||
'class' : 'logging.handlers.TimedRotatingFileHandler',
|
||||
'formatter' : 'standard',
|
||||
'filename' : LOG_DIR + '/mitx.log', # temporary location for proof of concept
|
||||
@@ -262,6 +262,7 @@ site.addsitedir(os.path.join(os.path.dirname(askbot.__file__), 'deps'))
|
||||
TEMPLATE_LOADERS = TEMPLATE_LOADERS + ('askbot.skins.loaders.filesystem_load_template_source',)
|
||||
|
||||
MIDDLEWARE_CLASSES = MIDDLEWARE_CLASSES + (
|
||||
'util.middleware.ExceptionLoggingMiddleware',
|
||||
'askbot.middleware.anon_user.ConnectToSessionMessagesMiddleware',
|
||||
'askbot.middleware.forum_mode.ForumModeMiddleware',
|
||||
'askbot.middleware.cancel.CancelActionMiddleware',
|
||||
|
||||
@@ -5,7 +5,7 @@ import os
|
||||
from django.conf import settings
|
||||
from django.http import Http404
|
||||
|
||||
def index(request, page=1):
|
||||
def index(request, page=0):
|
||||
if not request.user.is_authenticated():
|
||||
return redirect('/')
|
||||
return render_to_response('staticbook.html',{'page':int(page)})
|
||||
|
||||
@@ -5,10 +5,11 @@ from django.conf import settings
|
||||
import views
|
||||
|
||||
class TrackMiddleware:
|
||||
def process_request (self, request):
|
||||
def process_request(self, request):
|
||||
try:
|
||||
# We're already logging events
|
||||
if request.META['PATH_INFO'] == '/event':
|
||||
# We're already logging events, and we don't want to capture user
|
||||
# names/passwords.
|
||||
if request.META['PATH_INFO'] in ['/event', '/login']:
|
||||
return
|
||||
|
||||
event = { 'GET' : dict(request.GET),
|
||||
|
||||
2
urls.py
2
urls.py
@@ -19,7 +19,7 @@ urlpatterns = ('',
|
||||
url(r'^activate/(?P<key>[^/]*)$', 'auth.views.activate_account'),
|
||||
url(r'^$', 'auth.views.index'),
|
||||
url(r'^password_reset/$', 'django.contrib.auth.views.password_reset',
|
||||
dict(from_email='6002-admin@mit.edu'),name='auth_password_reset'),
|
||||
dict(from_email='registration@mitx.mit.edu'),name='auth_password_reset'),
|
||||
url(r'^password_change/$',django.contrib.auth.views.password_change,name='auth_password_change'),
|
||||
url(r'^password_change_done/$',django.contrib.auth.views.password_change_done,name='auth_password_change_done'),
|
||||
url(r'^password_reset_confirm/(?P<uidb36>[0-9A-Za-z]+)-(?P<token>.+)/$',django.contrib.auth.views.password_reset_confirm,
|
||||
|
||||
13
util/middleware.py
Normal file
13
util/middleware.py
Normal file
@@ -0,0 +1,13 @@
|
||||
import logging
|
||||
|
||||
from django.http import HttpResponse
|
||||
|
||||
log = logging.getLogger("mitx")
|
||||
|
||||
class ExceptionLoggingMiddleware(object):
|
||||
"""Just here to log unchecked exceptions that go all the way up the Django
|
||||
stack"""
|
||||
|
||||
def process_exception(self, request, exception):
|
||||
log.exception(exception)
|
||||
return HttpResponse("Server Error - Please try again later.")
|
||||
Reference in New Issue
Block a user