Commit bc823d3b by Calen Pennington

Merge branch 'master' into asset-pipeline

Conflicts:
	djangoapps/multicourse/__init__.py
	requirements.txt
	sass/_info.scss
	sass/_textbook.scss
	sass/courseware/_sequence-nav.scss
	sass/courseware/_video.scss
	sass/index/_base.scss
	sass/index/_extends.scss
	sass/index/_footer.scss
	sass/index/_header.scss
	sass/index/_index.scss
	sass/index/_variables.scss
	sass/layout/_calculator.scss
	sass/print.scss
	static/css
	static/css/application.css
	static/css/marketing.css
	templates/main.html
	templates/marketing.html
	templates/sass/index/_base.scss
	templates/sass/index/_extends.scss
	templates/sass/index/_footer.scss
	templates/sass/index/_header.scss
	templates/sass/index/_index.scss
	templates/sass/index/_variables.scss
	templates/sass/marketing/_base.scss
	templates/sass/marketing/_extends.scss
	templates/sass/marketing/_footer.scss
	templates/sass/marketing/_header.scss
	templates/sass/marketing/_index.scss
	templates/sass/marketing/_variables.scss
parents e8c4d1db cc6e0c55
......@@ -4,6 +4,9 @@
*.swp
*.orig
*.DS_Store
:2e_*
:2e#
.AppleDouble
database.sqlite
courseware/static/js/mathjax/*
db.newaskbot
......
......@@ -78,17 +78,23 @@ def evaluator(variables, functions, string, cs=False):
# log.debug("functions: {0}".format(functions))
# log.debug("string: {0}".format(string))
def lower_dict(d):
return dict([(k.lower(), d[k]) for k in d])
all_variables = copy.copy(default_variables)
all_variables.update(variables)
all_functions = copy.copy(default_functions)
if not cs:
all_variables = lower_dict(all_variables)
all_functions = lower_dict(all_functions)
all_variables.update(variables)
all_functions.update(functions)
if not cs:
string_cs = string.lower()
for v in all_variables.keys():
all_variables[v.lower()]=all_variables[v]
for f in all_functions.keys():
all_functions[f.lower()]=all_functions[f]
all_functions = lower_dict(all_functions)
all_variables = lower_dict(all_variables)
CasedLiteral = CaselessLiteral
else:
string_cs = string
......
import math
import operator
from numpy import eye, array
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")
'''
courseware/content_parser.py
This file interfaces between all courseware modules and the top-level course.xml file for a course.
Does some caching (to be explained).
'''
import hashlib
import json
import logging
import os
import re
......@@ -14,9 +22,11 @@ try: # This lets us do __name__ == ='__main__'
from student.models import UserProfile
from student.models import UserTestGroup
from mitxmako.shortcuts import render_to_response, render_to_string
from mitxmako.shortcuts import render_to_string
from util.cache import cache
from multicourse import multicourse_settings
except:
print "Could not import/content_parser"
settings = None
''' This file will eventually form an abstraction layer between the
......@@ -97,20 +107,9 @@ def item(l, default="", process=lambda x:x):
def id_tag(course):
''' Tag all course elements with unique IDs '''
old_ids = {'video':'youtube',
'problem':'filename',
'sequential':'id',
'html':'filename',
'vertical':'id',
'tab':'id',
'schematic':'id',
'book' : 'id'}
import courseware.modules
default_ids = courseware.modules.get_default_ids()
#print default_ids, old_ids
#print default_ids == old_ids
# Tag elements with unique IDs
elements = course.xpath("|".join(['//'+c for c in default_ids]))
for elem in elements:
......@@ -153,6 +152,9 @@ def propogate_downward_tag(element, attribute_name, parent_attribute = None):
return
def user_groups(user):
if not user.is_authenticated():
return []
# TODO: Rewrite in Django
key = 'user_group_names_{user.id}'.format(user=user)
cache_expiration = 60 * 60 # one hour
......@@ -177,15 +179,23 @@ def course_xml_process(tree):
propogate_downward_tag(tree, "due")
propogate_downward_tag(tree, "graded")
propogate_downward_tag(tree, "graceperiod")
propogate_downward_tag(tree, "showanswer")
propogate_downward_tag(tree, "rerandomize")
return tree
def course_file(user):
def course_file(user,coursename=None):
''' Given a user, return course.xml'''
#import logging
#log = logging.getLogger("tracking")
#log.info( "DEBUG: cf:"+str(user) )
filename = UserProfile.objects.get(user=user).courseware # user.profile_cache.courseware
if user.is_authenticated():
filename = UserProfile.objects.get(user=user).courseware # user.profile_cache.courseware
else:
filename = 'guest_course.xml'
# if a specific course is specified, then use multicourse to get the right path to the course XML directory
if coursename and settings.ENABLE_MULTICOURSE:
xp = multicourse_settings.get_course_xmlpath(coursename)
filename = xp + filename # prefix the filename with the path
groups = user_groups(user)
options = {'dev_content':settings.DEV_CONTENT,
'groups' : groups}
......@@ -207,13 +217,24 @@ def course_file(user):
return tree
def section_file(user, section):
''' Given a user and the name of a section, return that section
def section_file(user, section, coursename=None, dironly=False):
'''
Given a user and the name of a section, return that section.
This is done specific to each course.
If dironly=True then return the sections directory.
'''
filename = section+".xml"
if filename not in os.listdir(settings.DATA_DIR + '/sections/'):
print filename+" not in "+str(os.listdir(settings.DATA_DIR + '/sections/'))
# if a specific course is specified, then use multicourse to get the right path to the course XML directory
xp = ''
if coursename and settings.ENABLE_MULTICOURSE: xp = multicourse_settings.get_course_xmlpath(coursename)
dirname = settings.DATA_DIR + xp + '/sections/'
if dironly: return dirname
if filename not in os.listdir(dirname):
print filename+" not in "+str(os.listdir(dirname))
return None
options = {'dev_content':settings.DEV_CONTENT,
......@@ -223,7 +244,7 @@ def section_file(user, section):
return tree
def module_xml(user, module, id_tag, module_id):
def module_xml(user, module, id_tag, module_id, coursename=None):
''' Get XML for a module based on module and module_id. Assumes
module occurs once in courseware XML file or hidden section. '''
# Sanitize input
......@@ -236,14 +257,15 @@ def module_xml(user, module, id_tag, module_id):
id_tag=id_tag,
id=module_id)
#result_set=doc.xpathEval(xpath_search)
doc = course_file(user)
section_list = (s[:-4] for s in os.listdir(settings.DATA_DIR+'/sections') if s[-4:]=='.xml')
doc = course_file(user,coursename)
sdirname = section_file(user,'',coursename,True) # get directory where sections information is stored
section_list = (s[:-4] for s in os.listdir(sdirname) if s[-4:]=='.xml')
result_set=doc.xpath(xpath_search)
if len(result_set)<1:
for section in section_list:
try:
s = section_file(user, section)
s = section_file(user, section, coursename)
except etree.XMLSyntaxError:
ex= sys.exc_info()
raise ContentException("Malformed XML in " + section+ "("+str(ex[1].msg)+")")
......
GRADER = [
{
'type' : "Homework",
'min_count' : 12,
'drop_count' : 2,
'short_label' : "HW",
'weight' : 0.15,
},
{
'type' : "Lab",
'min_count' : 12,
'drop_count' : 2,
'category' : "Labs",
'weight' : 0.15
},
{
'type' : "Midterm",
'name' : "Midterm Exam",
'short_label' : "Midterm",
'weight' : 0.3,
},
{
'type' : "Final",
'name' : "Final Exam",
'short_label' : "Final",
'weight' : 0.4,
}
]
......@@ -6,9 +6,9 @@ from django.core.management.base import BaseCommand
from django.conf import settings
from django.contrib.auth.models import User
from mitx.courseware.content_parser import course_file
import mitx.courseware.module_render
import mitx.courseware.modules
from courseware.content_parser import course_file
import courseware.module_render
import courseware.modules
class Command(BaseCommand):
help = "Does basic validity tests on course.xml."
......@@ -25,15 +25,15 @@ class Command(BaseCommand):
check = False
print "Confirming all modules render. Nothing should print during this step. "
for module in course.xpath('//problem|//html|//video|//vertical|//sequential|/tab'):
module_class=mitx.courseware.modules.modx_modules[module.tag]
module_class = courseware.modules.modx_modules[module.tag]
# TODO: Abstract this out in render_module.py
try:
instance=module_class(etree.tostring(module),
module.get('id'),
ajax_url='',
state=None,
track_function = lambda x,y,z:None,
render_function = lambda x: {'content':'','destroy_js':'','init_js':'','type':'video'})
module_class(etree.tostring(module),
module.get('id'),
ajax_url='',
state=None,
track_function = lambda x,y,z:None,
render_function = lambda x: {'content':'','destroy_js':'','init_js':'','type':'video'})
except:
print "==============> Error in ", etree.tostring(module)
check = False
......
import StringIO
import json
import logging
import os
import sys
import sys
import urllib
import uuid
from lxml import etree
from django.conf import settings
from django.contrib.auth.models import User
from django.core.context_processors import csrf
from django.db import connection
from django.http import Http404
from django.http import HttpResponse
from django.shortcuts import redirect
from django.template import Context
from django.template import Context, loader
from mitxmako.shortcuts import render_to_response, render_to_string
from fs.osfs import OSFS
from django.conf import settings
from mitxmako.shortcuts import render_to_string
from models import StudentModule
from student.models import UserProfile
import track.views
import courseware.content_parser as content_parser
import courseware.modules
log = logging.getLogger("mitx.courseware")
class I4xSystem(object):
'''
This is an abstraction such that x_modules can function independent
of the courseware (e.g. import into other types of courseware, LMS,
or if we want to have a sandbox server for user-contributed content)
'''
def __init__(self, ajax_url, track_function, render_function, filestore=None):
self.ajax_url = ajax_url
self.track_function = track_function
if not filestore:
self.filestore = OSFS(settings.DATA_DIR)
self.render_function = render_function
self.exception404 = Http404
def __repr__(self):
return repr(self.__dict__)
def __str__(self):
return str(self.__dict__)
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
......@@ -51,60 +60,19 @@ def make_track_function(request):
return track.views.server_track(request, event_type, event, page='x_module')
return f
def modx_dispatch(request, module=None, dispatch=None, id=None):
''' Generic view for extensions. '''
if not request.user.is_authenticated():
return redirect('/')
# Grab the student information for the module from the database
s = StudentModule.objects.filter(student=request.user,
module_id=id)
#s = StudentModule.get_with_caching(request.user, id)
if len(s) == 0 or s is None:
log.debug("Couldnt find module for user and id " + str(module) + " " + str(request.user) + " "+ str(id))
raise Http404
s = s[0]
oldgrade = s.grade
oldstate = s.state
dispatch=dispatch.split('?')[0]
ajax_url = '/modx/'+module+'/'+id+'/'
# Grab the XML corresponding to the request from course.xml
xml = content_parser.module_xml(request.user, module, 'id', id)
# Create the module
instance=courseware.modules.get_module_class(module)(xml,
id,
ajax_url=ajax_url,
state=oldstate,
track_function = make_track_function(request),
render_function = None)
# Let the module handle the AJAX
ajax_return=instance.handle_ajax(dispatch, request.POST)
# Save the state back to the database
s.state=instance.get_state()
if instance.get_score():
s.grade=instance.get_score()['score']
if s.grade != oldgrade or s.state != oldstate:
s.save()
# Return whatever the module wanted to return to the client/caller
return HttpResponse(ajax_return)
def grade_histogram(module_id):
''' Print out a histogram of grades on a given problem.
Part of staff member debug info.
'''
from django.db import connection, transaction
from django.db import connection
cursor = connection.cursor()
cursor.execute("select courseware_studentmodule.grade,COUNT(courseware_studentmodule.student_id) from courseware_studentmodule where courseware_studentmodule.module_id=%s group by courseware_studentmodule.grade", [module_id])
grades = list(cursor.fetchall())
print grades
grades.sort(key=lambda x:x[0]) # Probably not necessary
if (len(grades) == 1 and grades[0][0] == None):
return []
return grades
def render_x_module(user, request, xml_module, module_object_preload):
......@@ -125,31 +93,51 @@ def render_x_module(user, request, xml_module, module_object_preload):
else:
state = smod.state
# get coursename if stored
if 'coursename' in request.session: coursename = request.session['coursename']
else: coursename = None
# Create a new instance
ajax_url = '/modx/'+module_type+'/'+module_id+'/'
instance=module_class(etree.tostring(xml_module),
ajax_url = settings.MITX_ROOT_URL + '/modx/'+module_type+'/'+module_id+'/'
system = I4xSystem(track_function = make_track_function(request),
render_function = lambda x: render_module(user, request, x, module_object_preload),
ajax_url = ajax_url,
filestore = None
)
instance=module_class(system,
etree.tostring(xml_module),
module_id,
ajax_url=ajax_url,
state=state,
track_function = make_track_function(request),
render_function = lambda x: render_module(user, request, x, module_object_preload))
state=state)
# If instance wasn't already in the database, create it
if not smod:
# If instance wasn't already in the database, and this
# isn't a guest user, create it
if not smod and user.is_authenticated():
smod=StudentModule(student=user,
module_type = module_type,
module_id=module_id,
state=instance.get_state())
smod.save()
module_object_preload.append(smod)
# Grab content
content = instance.get_html()
init_js = instance.get_init_js()
destory_js = instance.get_destroy_js()
# special extra information about each problem, only for users who are staff
if user.is_staff:
histogram = grade_histogram(module_id)
render_histogram = len(histogram) > 0
content=content+render_to_string("staff_problem_info.html", {'xml':etree.tostring(xml_module),
'histogram':grade_histogram(module_id)})
'module_id' : module_id,
'render_histogram' : render_histogram})
if render_histogram:
init_js = init_js+render_to_string("staff_problem_histogram.js", {'histogram' : histogram,
'module_id' : module_id})
content = {'content':content,
"destroy_js":instance.get_destroy_js(),
'init_js':instance.get_init_js(),
"destroy_js":destory_js,
'init_js':init_js,
'type':module_type}
return content
......
......@@ -16,13 +16,12 @@ import traceback
from lxml import etree
## TODO: Abstract out from Django
from django.conf import settings
from mitxmako.shortcuts import render_to_response, render_to_string
from django.http import Http404
from mitxmako.shortcuts import render_to_string
from x_module import XModule
from courseware.capa.capa_problem import LoncapaProblem, StudentInputError
import courseware.content_parser as content_parser
from multicourse import multicourse_settings
log = logging.getLogger("mitx.courseware")
......@@ -92,10 +91,13 @@ class Module(XModule):
# User submitted a problem, and hasn't reset. We don't want
# more submissions.
if self.lcp.done and self.rerandomize == "always":
#print "!"
check_button = False
save_button = False
# Only show the reset button if pressing it will show different values
if self.rerandomize != 'always':
reset_button = False
# User hasn't submitted an answer yet -- we don't want resets
if not self.lcp.done:
reset_button = False
......@@ -114,25 +116,26 @@ class Module(XModule):
if len(explain) == 0:
explain = False
html=render_to_string('problem.html',
{'problem' : content,
'id' : self.item_id,
'check_button' : check_button,
'reset_button' : reset_button,
'save_button' : save_button,
'answer_available' : self.answer_available(),
'ajax_url' : self.ajax_url,
'attempts_used': self.attempts,
'attempts_allowed': self.max_attempts,
'explain': explain
})
context = {'problem' : content,
'id' : self.item_id,
'check_button' : check_button,
'reset_button' : reset_button,
'save_button' : save_button,
'answer_available' : self.answer_available(),
'ajax_url' : self.ajax_url,
'attempts_used': self.attempts,
'attempts_allowed': self.max_attempts,
'explain': explain,
}
html=render_to_string('problem.html', context)
if encapsulate:
html = '<div id="main_{id}">'.format(id=self.item_id)+html+"</div>"
return html
def __init__(self, xml, item_id, ajax_url=None, track_url=None, state=None, track_function=None, render_function = None, meta = None):
XModule.__init__(self, xml, item_id, ajax_url, track_url, state, track_function, render_function)
def __init__(self, system, xml, item_id, state=None):
XModule.__init__(self, system, xml, item_id, state)
self.attempts = 0
self.max_attempts = None
......@@ -185,17 +188,24 @@ class Module(XModule):
if state!=None and 'attempts' in state:
self.attempts=state['attempts']
self.filename=content_parser.item(dom2.xpath('/problem/@filename'))
filename=settings.DATA_DIR+"/problems/"+self.filename+".xml"
self.filename="problems/"+content_parser.item(dom2.xpath('/problem/@filename'))+".xml"
self.name=content_parser.item(dom2.xpath('/problem/@name'))
self.weight=content_parser.item(dom2.xpath('/problem/@weight'))
if self.rerandomize == 'never':
seed = 1
else:
seed = None
self.lcp=LoncapaProblem(filename, self.item_id, state, seed = seed)
try:
fp = self.filestore.open(self.filename)
except Exception,err:
print '[courseware.capa.capa_module.Module.init] error %s: cannot open file %s' % (err,self.filename)
raise Exception,err
self.lcp=LoncapaProblem(fp, self.item_id, state, seed = seed)
def handle_ajax(self, dispatch, get):
'''
This is called by courseware.module_render, to handle an AJAX call. "get" is request.POST
'''
if dispatch=='problem_get':
response = self.get_problem(get)
elif False: #self.close_date >
......@@ -241,17 +251,23 @@ class Module(XModule):
return True
if self.show_answer == 'closed' and not self.closed():
return False
print "aa", self.show_answer
raise Http404
if self.show_answer == 'always':
return True
raise self.system.exception404 #TODO: Not 404
def get_answer(self, get):
'''
For the "show answer" button.
TODO: show answer events should be logged here, not just in the problem.js
'''
if not self.answer_available():
raise Http404
raise self.system.exception404
else:
return json.dumps(self.lcp.get_question_answers(),
answers = self.lcp.get_question_answers()
return json.dumps(answers,
cls=ComplexEncoder)
# Figure out if we should move these to capa_problem?
def get_problem(self, get):
''' Same as get_problem_html -- if we want to reconfirm we
......@@ -265,66 +281,56 @@ class Module(XModule):
event_info['state'] = self.lcp.get_state()
event_info['filename'] = self.filename
# make a dict of all the student responses ("answers").
answers=dict()
# input_resistor_1 ==> resistor_1
for key in get:
answers['_'.join(key.split('_')[1:])]=get[key]
# print "XXX", answers, get
event_info['answers']=answers
# Too late. Cannot submit
if self.closed():
event_info['failure']='closed'
self.tracker('save_problem_check_fail', event_info)
print "cp"
raise Http404
raise self.system.exception404
# Problem submitted. Student should reset before checking
# again.
if self.lcp.done and self.rerandomize == "always":
event_info['failure']='unreset'
self.tracker('save_problem_check_fail', event_info)
print "cpdr"
raise Http404
raise self.system.exception404
try:
old_state = self.lcp.get_state()
lcp_id = self.lcp.problem_id
filename = self.lcp.filename
correct_map = self.lcp.grade_answers(answers)
except StudentInputError as inst:
self.lcp = LoncapaProblem(filename, id=lcp_id, state=old_state)
self.lcp = LoncapaProblem(self.filestore.open(self.filename), id=lcp_id, state=old_state)
traceback.print_exc()
# print {'error':sys.exc_info(),
# 'answers':answers,
# 'seed':self.lcp.seed,
# 'filename':self.lcp.filename}
return json.dumps({'success':inst.message})
except:
self.lcp = LoncapaProblem(filename, id=lcp_id, state=old_state)
self.lcp = LoncapaProblem(self.filestore.open(self.filename), id=lcp_id, state=old_state)
traceback.print_exc()
raise Exception,"error in capa_module"
return json.dumps({'success':'Unknown Error'})
self.attempts = self.attempts + 1
self.lcp.done=True
success = 'correct'
for i in correct_map:
if correct_map[i]!='correct':
success = 'incorrect'
js=json.dumps({'correct_map' : correct_map,
'success' : success})
event_info['correct_map']=correct_map
event_info['success']=success
self.tracker('save_problem_check', event_info)
return js
return json.dumps({'success': success,
'contents': self.get_problem_html(encapsulate=False)})
def save_problem(self, get):
event_info = dict()
......@@ -382,8 +388,7 @@ class Module(XModule):
self.lcp.questions=dict() # Detailed info about questions in problem instance. TODO: Should be by id and not lid.
self.lcp.seed=None
filename=settings.DATA_DIR+"problems/"+self.filename+".xml"
self.lcp=LoncapaProblem(filename, self.item_id, self.lcp.get_state())
self.lcp=LoncapaProblem(self.filestore.open(self.filename), self.item_id, self.lcp.get_state())
event_info['new_state']=self.lcp.get_state()
self.tracker('reset_problem', event_info)
......
import json
## TODO: Abstract out from Django
from django.conf import settings
from mitxmako.shortcuts import render_to_response, render_to_string
from x_module import XModule
......@@ -24,13 +22,13 @@ class Module(XModule):
textlist=[i for i in textlist if type(i)==str]
return "".join(textlist)
try:
filename=settings.DATA_DIR+"html/"+self.filename
return open(filename).read()
filename="html/"+self.filename
return self.filestore.open(filename).read()
except: # For backwards compatibility. TODO: Remove
return render_to_string(self.filename, {'id': 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)
def __init__(self, system, xml, item_id, state=None):
XModule.__init__(self, system, xml, item_id, state)
xmltree=etree.fromstring(xml)
self.filename = None
filename_l=xmltree.xpath("/html/@filename")
......
......@@ -19,6 +19,6 @@ class Module(XModule):
def get_html(self):
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):
XModule.__init__(self, xml, item_id, ajax_url, track_url, state, render_function)
def __init__(self, system, xml, item_id, state=None):
XModule.__init__(self, system, xml, item_id, state)
......@@ -2,10 +2,7 @@ import json
from lxml import etree
## TODO: Abstract out from Django
from django.http import Http404
from django.conf import settings
from mitxmako.shortcuts import render_to_response, render_to_string
from mitxmako.shortcuts import render_to_string
from x_module import XModule
......@@ -38,12 +35,10 @@ class Module(XModule):
return self.destroy_js
def handle_ajax(self, dispatch, get):
print "GET", get
print "DISPATCH", dispatch
if dispatch=='goto_position':
self.position = int(get['position'])
return json.dumps({'success':True})
raise Http404()
raise self.system.exception404
def render(self):
if self.rendered:
......@@ -107,14 +102,13 @@ class Module(XModule):
self.rendered = True
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.xmltree=etree.fromstring(xml)
def __init__(self, system, xml, item_id, state=None):
XModule.__init__(self, system, xml, item_id, state)
self.xmltree = etree.fromstring(xml)
self.position = 1
if state!=None:
if state != None:
state = json.loads(state)
if 'position' in state: self.position = int(state['position'])
......
......@@ -14,16 +14,16 @@ class Module(XModule):
@classmethod
def get_xml_tags(c):
## TODO: Abstract out from filesystem
tags = os.listdir(settings.DATA_DIR+'/custom_tags')
return tags
def get_html(self):
return self.html
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)
def __init__(self, system, xml, item_id, state=None):
XModule.__init__(self, system, xml, item_id, state)
xmltree = etree.fromstring(xml)
filename = xmltree.tag
params = dict(xmltree.items())
# print params
self.html = render_to_string(filename, params, namespace = 'custom_tags')
import json
## TODO: Abstract out from Django
from django.conf import settings
from mitxmako.shortcuts import render_to_response, render_to_string
from x_module import XModule
......@@ -26,8 +24,9 @@ class Module(XModule):
def get_destroy_js(self):
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):
XModule.__init__(self, xml, item_id, ajax_url, track_url, state, track_function, render_function)
def __init__(self, system, xml, item_id, state=None):
XModule.__init__(self, system, xml, item_id, state)
xmltree=etree.fromstring(xml)
self.contents=[(e.get("name"),self.render_function(e)) \
for e in xmltree]
......
......@@ -3,8 +3,6 @@ import logging
from lxml import etree
## TODO: Abstract out from Django
from django.conf import settings
from mitxmako.shortcuts import render_to_response, render_to_string
from x_module import XModule
......@@ -42,7 +40,8 @@ class Module(XModule):
return render_to_string('video.html',{'streams':self.video_list(),
'id':self.item_id,
'position':self.position,
'name':self.name})
'name':self.name,
'annotations':self.annotations})
def get_init_js(self):
'''JavaScript code to be run when problem is shown. Be aware
......@@ -52,19 +51,23 @@ class Module(XModule):
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})
'position':self.position})+self.annotations_init
def get_destroy_js(self):
return "videoDestroy(\"{0}\");".format(self.item_id)
return "videoDestroy(\"{0}\");".format(self.item_id)+self.annotations_destroy
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.name = etree.XML(xml).get('name')
def __init__(self, system, xml, item_id, state=None):
XModule.__init__(self, system, xml, item_id, state)
xmltree=etree.fromstring(xml)
self.youtube = xmltree.get('youtube')
self.name = xmltree.get('name')
self.position = 0
if state != None:
state = json.loads(state)
if 'position' in state:
self.position = int(float(state['position']))
#log.debug("POSITION IN STATE")
#log.debug(u"LOAD POSITION {0}".format(self.position))
self.annotations=[(e.get("name"),self.render_function(e)) \
for e in xmltree]
self.annotations_init="".join([e[1]['init_js'] for e in self.annotations if 'init_js' in e[1]])
self.annotations_destroy="".join([e[1]['destroy_js'] for e in self.annotations if 'destroy_js' in e[1]])
......@@ -45,13 +45,17 @@ class XModule(object):
get is a dictionary-like object '''
return ""
def __init__(self, xml, item_id, ajax_url=None, track_url=None, state=None, track_function=None, render_function = None):
def __init__(self, system, xml, item_id, track_url=None, state=None):
''' In most cases, you must pass state or xml'''
self.xml = xml
self.item_id = item_id
self.ajax_url = ajax_url
self.track_url = track_url
self.state = state
self.tracker = track_function
self.render_function = render_function
if system:
## These are temporary; we really should go
## through self.system.
self.ajax_url = system.ajax_url
self.tracker = system.track_function
self.filestore = system.filestore
self.render_function = system.render_function
self.system = system
<problem>
<text><p>
Two skiers are on frictionless black diamond ski slopes.
Hello</p></text>
<imageresponse max="1" loncapaid="11">
<imageinput src="/static/Physics801/Figures/Skier-conservation of energy.jpg" width="560" height="388" rectangle="(490,11)-(556,98)"/>
<text>Click on the image where the top skier will stop momentarily if the top skier starts from rest.</text>
<imageinput src="/static/Physics801/Figures/Skier-conservation of energy.jpg" width="560" height="388" rectangle="(242,202)-(296,276)"/>
<text>Click on the image where the lower skier will stop momentarily if the lower skier starts from rest.</text>
<hintgroup showoncorrect="no">
<text><p>Use conservation of energy.</p></text>
</hintgroup>
</imageresponse>
</problem>
\ No newline at end of file
<problem>
<multiplechoiceresponse>
<choicegroup>
<choice correct="false" >
<startouttext />This is foil One.<endouttext />
</choice>
<choice correct="false" >
<startouttext />This is foil Two.<endouttext />
</choice>
<choice correct="true" >
<startouttext />This is foil Three.<endouttext />
</choice>
<choice correct="false">
<startouttext />This is foil Four.<endouttext />
</choice>
<choice correct="false">
<startouttext />This is foil Five.<endouttext />
</choice>
</choicegroup>
</multiplechoiceresponse>
</problem>
<problem>
<multiplechoiceresponse>
<choicegroup>
<choice correct="false" name="foil1">
<startouttext />This is foil One.<endouttext />
</choice>
<choice correct="false" name="foil2">
<startouttext />This is foil Two.<endouttext />
</choice>
<choice correct="true" name="foil3">
<startouttext />This is foil Three.<endouttext />
</choice>
<choice correct="false" name="foil4">
<startouttext />This is foil Four.<endouttext />
</choice>
<choice correct="false" name="foil5">
<startouttext />This is foil Five.<endouttext />
</choice>
</choicegroup>
</multiplechoiceresponse>
</problem>
<problem>
<text>
<p>
Why do bicycles benefit from having larger wheels when going up a bump as shown in the picture? <br/>
Assume that for both bicycles:<br/>
1.) The tires have equal air pressure.<br/>
2.) The bicycles never leave the contact with the bump.<br/>
3.) The bicycles have the same mass. The bicycle tires (regardless of size) have the same mass.<br/>
</p>
</text>
<optionresponse texlayout="horizontal" max="10" randomize="yes">
<ul>
<li>
<text>
<p>The bicycles with larger wheels have more time to go over the bump. This decreases the magnitude of the force needed to lift the bicycle.</p>
</text>
<optioninput name="Foil1" location="random" options="('True','False')" correct="True">
</optioninput>
</li>
<li>
<text>
<p>The bicycles with larger wheels always have a smaller vertical displacement regardless of speed.</p>
</text>
<optioninput name="Foil2" location="random" options="('True','False')" correct="False">
</optioninput>
</li>
<li>
<text>
<p>The bicycles with larger wheels experience a force backward with less magnitude for the same amount of time.</p>
</text>
<optioninput name="Foil3" location="random" options="('True','False')" correct="False">
</optioninput>
</li>
<li>
<text>
<p>The bicycles with larger wheels experience a force backward with less magnitude for a greater amount of time.</p>
</text>
<optioninput name="Foil4" location="random" options="('True','False')" correct="True">
</optioninput>
</li>
<li>
<text>
<p>The bicycles with larger wheels have more kinetic energy turned into gravitational potential energy.</p>
</text>
<optioninput name="Foil5" location="random" options="('True','False')" correct="False">
</optioninput>
</li>
<li>
<text>
<p>The bicycles with larger wheels have more rotational kinetic energy, so the horizontal velocity of the biker changes less.</p>
</text>
<optioninput name="Foil6" location="random" options="('True','False')" correct="False">
</optioninput>
</li>
</ul>
<hintgroup showoncorrect="no">
<text>
<br/>
<br/>
</text>
</hintgroup>
</optionresponse>
</problem>
<problem>
<truefalseresponse max="10" randomize="yes">
<choicegroup>
<choice location="random" correct="true" name="foil1">
<startouttext />This is foil One.<endouttext />
</choice>
<choice location="random" correct="true" name="foil2">
<startouttext />This is foil Two.<endouttext />
</choice>
<choice location="random" correct="false" name="foil3">
<startouttext />This is foil Three.<endouttext />
</choice>
<choice location="random" correct="false" name="foil4">
<startouttext />This is foil Four.<endouttext />
</choice>
<choice location="random" correct="false" name="foil5">
<startouttext />This is foil Five.<endouttext />
</choice>
</choicegroup>
</truefalseresponse>
</problem>
# multicourse/multicourse_settings.py
#
# central module for providing fixed settings (course name, number, title)
# for multiple courses. Loads this information from django.conf.settings
#
# Allows backward compatibility with settings configurations without
# multiple courses specified.
#
# The central piece of configuration data is the dict COURSE_SETTINGS, with
# keys being the COURSE_NAME (spaces ok), and the value being a dict of
# parameter,value pairs. The required parameters are:
#
# - number : course number (used in the simplewiki pages)
# - title : humanized descriptive course title
#
# Optional parameters:
#
# - xmlpath : path (relative to data directory) for this course (defaults to "")
#
# If COURSE_SETTINGS does not exist, then fallback to 6.002_Spring_2012 default,
# for now.
from django.conf import settings
#-----------------------------------------------------------------------------
# load course settings
if hasattr(settings,'COURSE_SETTINGS'): # in the future, this could be replaced by reading an XML file
COURSE_SETTINGS = settings.COURSE_SETTINGS
elif hasattr(settings,'COURSE_NAME'): # backward compatibility
COURSE_SETTINGS = {settings.COURSE_NAME: {'number': settings.COURSE_NUMBER,
'title': settings.COURSE_TITLE,
},
}
else: # default to 6.002_Spring_2012
COURSE_SETTINGS = {'6.002_Spring_2012': {'number': '6.002x',
'title': 'Circuits and Electronics',
},
}
#-----------------------------------------------------------------------------
# wrapper functions around course settings
def get_course_settings(coursename):
if not coursename:
if hasattr(settings,'COURSE_DEFAULT'):
coursename = settings.COURSE_DEFAULT
else:
coursename = '6.002_Spring_2012'
if coursename in COURSE_SETTINGS: return COURSE_SETTINGS[coursename]
coursename = coursename.replace(' ','_')
if coursename in COURSE_SETTINGS: return COURSE_SETTINGS[coursename]
return None
def is_valid_course(coursename):
return not (get_course_settings==None)
def get_course_property(coursename,property):
cs = get_course_settings(coursename)
if not cs: return '' # raise exception instead?
if property in cs: return cs[property]
return '' # default
def get_course_xmlpath(coursename):
return get_course_property(coursename,'xmlpath')
def get_course_title(coursename):
return get_course_property(coursename,'title')
def get_course_number(coursename):
return get_course_property(coursename,'number')
# multicourse/views.py
......@@ -9,7 +9,7 @@ from django.db.models import signals
from django.utils.translation import ugettext_lazy as _
from markdown import markdown
from settings import *
from wiki_settings import *
from util.cache import cache
......
......@@ -3,7 +3,7 @@ from django.conf import settings
from django.template.defaultfilters import stringfilter
from django.utils.http import urlquote as django_urlquote
from simplewiki.settings import *
from simplewiki.wiki_settings import *
register = template.Library()
......
# -*- coding: utf-8 -*-
import types
from django.conf import settings
from django.conf import settings as settings
from django.contrib.auth.decorators import login_required
from django.core.context_processors import csrf
from django.core.urlresolvers import get_callable
from django.core.urlresolvers import reverse
from django.db.models import Q
from django.http import Http404, HttpResponse, HttpResponseRedirect, HttpResponseServerError, HttpResponseForbidden, HttpResponseNotAllowed
from django.http import HttpResponse
from django.shortcuts import get_object_or_404
from django.shortcuts import redirect
from django.template import Context
from django.template import RequestContext, Context, loader
from django.http import HttpResponse, HttpResponseRedirect
from django.utils import simplejson
from django.utils.translation import ugettext_lazy as _
from mitxmako.shortcuts import render_to_response, render_to_string
from mako.lookup import TemplateLookup
from mako.template import Template
import mitxmako.middleware
from mitxmako.shortcuts import render_to_response
from multicourse import multicourse_settings
from models import * # TODO: Clean up
from settings import *
from models import Revision, Article, CreateArticleForm, RevisionFormWithTitle, RevisionForm
import wiki_settings
def view(request, wiki_url):
if not request.user.is_authenticated():
return redirect('/')
(article, path, err) = fetch_from_url(request, wiki_url)
if err:
return err
if 'coursename' in request.session: coursename = request.session['coursename']
else: coursename = None
course_number = multicourse_settings.get_course_number(coursename)
perm_err = check_permissions(request, article, check_read=True, check_deleted=True)
if perm_err:
return perm_err
......@@ -39,15 +32,12 @@ def view(request, wiki_url):
'wiki_write': article.can_write_l(request.user),
'wiki_attachments_write': article.can_attach(request.user),
'wiki_current_revision_deleted' : not (article.current_revision.deleted == 0),
'wiki_title' : article.title + " - MITX 6.002x Wiki"
'wiki_title' : article.title + " - MITX %s Wiki" % course_number
}
d.update(csrf(request))
return render_to_response('simplewiki_view.html', d)
def view_revision(request, revision_number, wiki_url, revision=None):
if not request.user.is_authenticated():
return redirect('/')
(article, path, err) = fetch_from_url(request, wiki_url)
if err:
return err
......@@ -76,8 +66,6 @@ def view_revision(request, revision_number, wiki_url, revision=None):
def root_redirect(request):
if not request.user.is_authenticated():
return redirect('/')
try:
root = Article.get_root()
except:
......@@ -87,8 +75,6 @@ def root_redirect(request):
return HttpResponseRedirect(reverse('wiki_view', args=(root.get_url())))
def create(request, wiki_url):
if not request.user.is_authenticated():
return redirect('/')
url_path = get_url_path(wiki_url)
......@@ -161,9 +147,6 @@ def create(request, wiki_url):
return render_to_response('simplewiki_edit.html', d)
def edit(request, wiki_url):
if not request.user.is_authenticated():
return redirect('/')
(article, path, err) = fetch_from_url(request, wiki_url)
if err:
return err
......@@ -173,7 +156,7 @@ def edit(request, wiki_url):
if perm_err:
return perm_err
if WIKI_ALLOW_TITLE_EDIT:
if wiki_settings.WIKI_ALLOW_TITLE_EDIT:
EditForm = RevisionFormWithTitle
else:
EditForm = RevisionForm
......@@ -195,7 +178,7 @@ def edit(request, wiki_url):
if not request.user.is_anonymous():
new_revision.revision_user = request.user
new_revision.save()
if WIKI_ALLOW_TITLE_EDIT:
if wiki_settings.WIKI_ALLOW_TITLE_EDIT:
new_revision.article.title = f.cleaned_data['title']
new_revision.article.save()
return HttpResponseRedirect(reverse('wiki_view', args=(article.get_url(),)))
......@@ -215,9 +198,6 @@ def edit(request, wiki_url):
return render_to_response('simplewiki_edit.html', d)
def history(request, wiki_url, page=1):
if not request.user.is_authenticated():
return redirect('/')
(article, path, err) = fetch_from_url(request, wiki_url)
if err:
return err
......@@ -302,9 +282,6 @@ def history(request, wiki_url, page=1):
def revision_feed(request, page=1):
if not request.user.is_superuser:
return redirect('/')
page_size = 10
try:
......@@ -332,8 +309,6 @@ def revision_feed(request, page=1):
return render_to_response('simplewiki_revision_feed.html', d)
def search_articles(request):
if not request.user.is_authenticated():
return redirect('/')
# blampe: We should check for the presence of other popular django search
# apps and use those if possible. Only fall back on this as a last resort.
# Adding some context to results (eg where matches were) would also be nice.
......@@ -380,9 +355,6 @@ def search_articles(request):
def search_add_related(request, wiki_url):
if not request.user.is_authenticated():
return redirect('/')
(article, path, err) = fetch_from_url(request, wiki_url)
if err:
return err
......@@ -435,9 +407,6 @@ def add_related(request, wiki_url):
return HttpResponseRedirect(reverse('wiki_view', args=(article.get_url(),)))
def remove_related(request, wiki_url, related_id):
if not request.user.is_authenticated():
return redirect('/')
(article, path, err) = fetch_from_url(request, wiki_url)
if err:
return err
......@@ -457,8 +426,6 @@ def remove_related(request, wiki_url, related_id):
return HttpResponseRedirect(reverse('wiki_view', args=(article.get_url(),)))
def random_article(request):
if not request.user.is_authenticated():
return redirect('/')
from random import randint
num_arts = Article.objects.count()
article = Article.objects.all()[randint(0, num_arts-1)]
......@@ -470,8 +437,6 @@ def encode_err(request, url):
return render_to_response('simplewiki_error.html', d)
def not_found(request, wiki_url):
if not request.user.is_authenticated():
return redirect('/')
"""Generate a NOT FOUND message for some URL"""
d = {'wiki_err_notfound': True,
'wiki_url': wiki_url}
......@@ -543,17 +508,22 @@ def check_permissions(request, article, check_read=False, check_write=False, che
# LOGIN PROTECTION #
####################
if WIKI_REQUIRE_LOGIN_VIEW:
view = login_required(view)
history = login_required(history)
# search_related = login_required(search_related)
# wiki_encode_err = login_required(wiki_encode_err)
if WIKI_REQUIRE_LOGIN_EDIT:
if wiki_settings.WIKI_REQUIRE_LOGIN_VIEW:
view = login_required(view)
history = login_required(history)
search_articles = login_required(search_articles)
root_redirect = login_required(root_redirect)
revision_feed = login_required(revision_feed)
random_article = login_required(random_article)
search_add_related = login_required(search_add_related)
not_found = login_required(not_found)
view_revision = login_required(view_revision)
if wiki_settings.WIKI_REQUIRE_LOGIN_EDIT:
create = login_required(create)
edit = login_required(edit)
add_related = login_required(add_related)
remove_related = login_required(remove_related)
if WIKI_CONTEXT_PREPROCESSORS:
settings.TEMPLATE_CONTEXT_PROCESSORS = settings.TEMPLATE_CONTEXT_PROCESSORS + WIKI_CONTEXT_PREPROCESSORS
if wiki_settings.WIKI_CONTEXT_PREPROCESSORS:
settings.TEMPLATE_CONTEXT_PROCESSORS += wiki_settings.WIKI_CONTEXT_PREPROCESSORS
......@@ -3,14 +3,21 @@ import os
from django.contrib.auth.decorators import login_required
from django.core.servers.basehttp import FileWrapper
from django.db.models.fields.files import FieldFile
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidden, Http404
from django.http import HttpResponse, HttpResponseForbidden, Http404
from django.template import loader, Context
from settings import * # TODO: Clean up
from models import Article, ArticleAttachment, get_attachment_filepath
from views import not_found, check_permissions, get_url_path, fetch_from_url
from simplewiki.settings import WIKI_ALLOW_ANON_ATTACHMENTS
from models import ArticleAttachment, get_attachment_filepath
from views import check_permissions, fetch_from_url
from wiki_settings import (
WIKI_ALLOW_ANON_ATTACHMENTS,
WIKI_ALLOW_ATTACHMENTS,
WIKI_ATTACHMENTS_MAX,
WIKI_ATTACHMENTS_ROOT,
WIKI_ATTACHMENTS_ALLOWED_EXTENSIONS,
WIKI_REQUIRE_LOGIN_VIEW,
WIKI_REQUIRE_LOGIN_EDIT,
)
def add_attachment(request, wiki_url):
......
# Create your views here.
import os
from django.conf import settings
from django.http import Http404
from django.shortcuts import redirect
from mitxmako.shortcuts import render_to_response, render_to_string
from django.contrib.auth.decorators import login_required
from mitxmako.shortcuts import render_to_response
@login_required
def index(request, page=0):
if not request.user.is_authenticated():
return redirect('/')
return render_to_response('staticbook.html',{'page':int(page)})
def index_shifted(request, page):
......
......@@ -10,14 +10,14 @@ from django.conf import settings
from django.contrib.auth import logout, authenticate, login
from django.contrib.auth.forms import PasswordResetForm
from django.contrib.auth.models import User
from django.contrib.auth.decorators import login_required
from django.core.context_processors import csrf
from django.core.mail import send_mail
from django.core.validators import validate_email, validate_slug, ValidationError
from django.db import connection
from django.db import IntegrityError
from django.http import HttpResponse, Http404
from django.shortcuts import redirect
from mitxmako.shortcuts import render_to_response, render_to_string
from mako import exceptions
from django_future.csrf import ensure_csrf_cookie
......@@ -93,12 +93,11 @@ def logout_user(request):
logout(request)
return redirect('/')
@login_required
@ensure_csrf_cookie
def change_setting(request):
''' JSON call to change a profile setting: Right now, location and language
'''
if not request.user.is_authenticated():
return redirect('/')
up = UserProfile.objects.get(user=request.user) #request.user.profile_cache
if 'location' in request.POST:
up.location=request.POST['location']
......@@ -162,15 +161,6 @@ def create_account(request, post_override=None):
# Confirm username and e-mail are unique. TODO: This should be in a transaction
if len(User.objects.filter(username=post_vars['username']))>0:
js['value']="An account with this username already exists."
return HttpResponse(json.dumps(js))
if len(User.objects.filter(email=post_vars['email']))>0:
js['value']="An account with this e-mail already exists."
return HttpResponse(json.dumps(js))
u=User(username=post_vars['username'],
email=post_vars['email'],
is_active=False)
......@@ -178,7 +168,20 @@ def create_account(request, post_override=None):
r=Registration()
# TODO: Rearrange so that if part of the process fails, the whole process fails.
# Right now, we can have e.g. no registration e-mail sent out and a zombie account
u.save()
try:
u.save()
except IntegrityError:
# Figure out the cause of the integrity error
if len(User.objects.filter(username=post_vars['username']))>0:
js['value']="An account with this username already exists."
return HttpResponse(json.dumps(js))
if len(User.objects.filter(email=post_vars['email']))>0:
js['value']="An account with this e-mail already exists."
return HttpResponse(json.dumps(js))
raise
r.register(u)
up = UserProfile(user=u)
......
......@@ -8,7 +8,8 @@ Common traits:
"""
import json
from common import *
from envs.logsettings import get_logger_config
from envs.common import *
############################### ALWAYS THE SAME ################################
DEBUG = False
......@@ -24,7 +25,6 @@ with open(ENV_ROOT / "env.json") as env_file:
ENV_TOKENS = json.load(env_file)
SITE_NAME = ENV_TOKENS['SITE_NAME']
CSRF_COOKIE_DOMAIN = ENV_TOKENS['CSRF_COOKIE_DOMAIN']
BOOK_URL = ENV_TOKENS['BOOK_URL']
MEDIA_URL = ENV_TOKENS['MEDIA_URL']
......@@ -32,10 +32,10 @@ LOG_DIR = ENV_TOKENS['LOG_DIR']
CACHES = ENV_TOKENS['CACHES']
LOGGING = logsettings.get_logger_config(LOG_DIR,
logging_env=ENV_TOKENS['LOGGING_ENV'],
syslog_addr=(ENV_TOKENS['SYSLOG_SERVER'], 514),
debug=False)
LOGGING = get_logger_config(LOG_DIR,
logging_env=ENV_TOKENS['LOGGING_ENV'],
syslog_addr=(ENV_TOKENS['SYSLOG_SERVER'], 514),
debug=False)
############################## SECURE AUTH ITEMS ###############################
# Secret things: passwords, access keys, etc.
......@@ -47,4 +47,4 @@ SECRET_KEY = AUTH_TOKENS['SECRET_KEY']
AWS_ACCESS_KEY_ID = AUTH_TOKENS["AWS_ACCESS_KEY_ID"]
AWS_SECRET_ACCESS_KEY = AUTH_TOKENS["AWS_SECRET_ACCESS_KEY"]
DATABASES = AUTH_TOKENS['DATABASES']
\ No newline at end of file
DATABASES = AUTH_TOKENS['DATABASES']
......@@ -24,8 +24,7 @@ import tempfile
import djcelery
from path import path
from askbotsettings import * # this is where LIVESETTINGS_OPTIONS comes from
import logsettings
from envs.askbotsettings import * # this is where LIVESETTINGS_OPTIONS comes from
################################### FEATURES ###################################
COURSEWARE_ENABLED = True
......@@ -81,6 +80,7 @@ TEMPLATE_DIRS = (
TEMPLATE_CONTEXT_PROCESSORS = (
'django.core.context_processors.request',
'askbot.context.application_settings',
'django.contrib.messages.context_processors.messages',
#'django.core.context_processors.i18n',
'askbot.user_messages.context_processors.user_messages',#must be before auth
'django.core.context_processors.auth', #this is required for admin
......@@ -113,7 +113,6 @@ TEMPLATE_DEBUG = False
# Site info
SITE_ID = 1
SITE_NAME = "localhost:8000"
CSRF_COOKIE_DOMAIN = '127.0.0.1'
HTTPS = 'on'
ROOT_URLCONF = 'mitx.urls'
IGNORABLE_404_ENDS = ('favicon.ico')
......@@ -134,7 +133,7 @@ STATIC_ROOT = ENV_ROOT / "staticfiles" # We don't run collectstatic -- this is t
# FIXME: We should iterate through the courses we have, adding the static
# contents for each of them. (Right now we just use symlinks.)
STATICFILES_DIRS = (
STATICFILES_DIRS = [
PROJECT_ROOT / "static",
ASKBOT_ROOT / "askbot" / "skins",
("circuits", DATA_DIR / "images"),
......@@ -143,7 +142,7 @@ STATICFILES_DIRS = (
# This is how you would use the textbook images locally
# ("book", ENV_ROOT / "book_images")
)
]
# Locale/Internationalization
TIME_ZONE = 'America/New_York' # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
......@@ -151,6 +150,9 @@ LANGUAGE_CODE = 'en' # http://www.i18nguy.com/unicode/language-identifiers.html
USE_I18N = True
USE_L10N = True
# Messages
MESSAGE_STORAGE = 'django.contrib.messages.storage.session.SessionStorage'
#################################### AWS #######################################
# S3BotoStorage insists on a timeout for uploaded assets. We should make it
# permanent instead, but rather than trying to figure out exactly where that
......@@ -179,8 +181,8 @@ CELERY_ALWAYS_EAGER = True
djcelery.setup_loader()
################################# SIMPLEWIKI ###################################
WIKI_REQUIRE_LOGIN_EDIT = True
WIKI_REQUIRE_LOGIN_VIEW = True
SIMPLE_WIKI_REQUIRE_LOGIN_EDIT = True
SIMPLE_WIKI_REQUIRE_LOGIN_VIEW = False
################################# Middleware ###################################
# List of finder classes that know how to find static files in
......
......@@ -2,7 +2,7 @@
These are debug machines used for content creators, so they're kind of a cross
between dev machines and AWS machines.
"""
from aws import *
from envs.aws import *
DEBUG = True
TEMPLATE_DEBUG = True
......
......@@ -7,16 +7,17 @@ sessions. Assumes structure:
/mitx # The location of this repo
/log # Where we're going to write log files
"""
from common import *
from envs.common import *
from envs.logsettings import get_logger_config
DEBUG = True
PIPELINE = True
TEMPLATE_DEBUG = True
LOGGING = logsettings.get_logger_config(ENV_ROOT / "log",
logging_env="dev",
tracking_filename="tracking.log",
debug=True)
LOGGING = get_logger_config(ENV_ROOT / "log",
logging_env="dev",
tracking_filename="tracking.log",
debug=True)
DATABASES = {
'default': {
......@@ -74,7 +75,8 @@ DEBUG_TOOLBAR_PANELS = (
############################ FILE UPLOADS (ASKBOT) #############################
DEFAULT_FILE_STORAGE = 'django.core.files.storage.FileSystemStorage'
MEDIA_ROOT = ENV_ROOT / "uploads"
MEDIA_URL = "/discussion/upfiles/"
MEDIA_URL = "/static/uploads/"
STATICFILES_DIRS.append(("uploads", MEDIA_ROOT))
FILE_UPLOAD_TEMP_DIR = ENV_ROOT / "uploads"
FILE_UPLOAD_HANDLERS = (
'django.core.files.uploadhandler.MemoryFileUploadHandler',
......
......@@ -13,7 +13,7 @@ Dir structure:
/log # Where we're going to write log files
"""
from dev import *
from envs.dev import *
DATABASES = {
'default': {
......
"""
This config file runs the simplest dev environment using sqlite, and db-based
sessions. Assumes structure:
/envroot/
/db # This is where it'll write the database file
/mitx # The location of this repo
/log # Where we're going to write log files
"""
from envs.common import *
from envs.logsettings import get_logger_config
STATIC_GRAB = True
LOGGING = get_logger_config(ENV_ROOT / "log",
logging_env="dev",
tracking_filename="tracking.log",
debug=False)
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': ENV_ROOT / "db" / "mitx.db",
}
}
CACHES = {
# This is the cache used for most things. Askbot will not work without a
# functioning cache -- it relies on caching to load its settings in places.
# In staging/prod envs, the sessions also live here.
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
'LOCATION': 'mitx_loc_mem_cache'
},
# The general cache is what you get if you use our util.cache. It's used for
# things like caching the course.xml file for different A/B test groups.
# We set it to be a DummyCache to force reloading of course.xml in dev.
# In staging environments, we would grab VERSION from data uploaded by the
# push process.
'general': {
'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
'KEY_PREFIX': 'general',
'VERSION': 4,
}
}
# Dummy secret key for dev
SECRET_KEY = '85920908f28904ed733fe576320db18cabd7b6cd'
############################ FILE UPLOADS (ASKBOT) #############################
DEFAULT_FILE_STORAGE = 'django.core.files.storage.FileSystemStorage'
MEDIA_ROOT = ENV_ROOT / "uploads"
MEDIA_URL = "/discussion/upfiles/"
FILE_UPLOAD_TEMP_DIR = ENV_ROOT / "uploads"
FILE_UPLOAD_HANDLERS = (
'django.core.files.uploadhandler.MemoryFileUploadHandler',
'django.core.files.uploadhandler.TemporaryFileUploadHandler',
)
"""
This config file runs the simplest dev environment using sqlite, and db-based
sessions. Assumes structure:
/envroot/
/db # This is where it'll write the database file
/mitx # The location of this repo
/log # Where we're going to write log files
"""
from envs.common import *
from envs.logsettings import get_logger_config
import os
INSTALLED_APPS = [
app
for app
in INSTALLED_APPS
if not app.startswith('askbot')
]
# Nose Test Runner
INSTALLED_APPS += ['django_nose']
NOSE_ARGS = ['--cover-erase', '--with-xunit', '--with-xcoverage', '--cover-html', '--cover-inclusive']
for app in os.listdir(PROJECT_ROOT / 'djangoapps'):
NOSE_ARGS += ['--cover-package', app]
TEST_RUNNER = 'django_nose.NoseTestSuiteRunner'
# Local Directories
TEST_ROOT = path("test_root")
COURSES_ROOT = TEST_ROOT / "data"
DATA_DIR = COURSES_ROOT
MAKO_TEMPLATES['course'] = [DATA_DIR]
MAKO_TEMPLATES['sections'] = [DATA_DIR / 'sections']
MAKO_TEMPLATES['custom_tags'] = [DATA_DIR / 'custom_tags']
MAKO_TEMPLATES['main'] = [PROJECT_ROOT / 'templates',
DATA_DIR / 'info',
DATA_DIR / 'problems']
LOGGING = get_logger_config(TEST_ROOT / "log",
logging_env="dev",
tracking_filename="tracking.log",
debug=True)
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': PROJECT_ROOT / "db" / "mitx.db",
}
}
CACHES = {
# This is the cache used for most things. Askbot will not work without a
# functioning cache -- it relies on caching to load its settings in places.
# In staging/prod envs, the sessions also live here.
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
'LOCATION': 'mitx_loc_mem_cache'
},
# The general cache is what you get if you use our util.cache. It's used for
# things like caching the course.xml file for different A/B test groups.
# We set it to be a DummyCache to force reloading of course.xml in dev.
# In staging environments, we would grab VERSION from data uploaded by the
# push process.
'general': {
'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
'KEY_PREFIX': 'general',
'VERSION': 4,
}
}
# Dummy secret key for dev
SECRET_KEY = '85920908f28904ed733fe576320db18cabd7b6cd'
############################ FILE UPLOADS (ASKBOT) #############################
DEFAULT_FILE_STORAGE = 'django.core.files.storage.FileSystemStorage'
MEDIA_ROOT = PROJECT_ROOT / "uploads"
MEDIA_URL = "/static/uploads/"
STATICFILES_DIRS.append(("uploads", MEDIA_ROOT))
FILE_UPLOAD_TEMP_DIR = PROJECT_ROOT / "uploads"
FILE_UPLOAD_HANDLERS = (
'django.core.files.uploadhandler.MemoryFileUploadHandler',
'django.core.files.uploadhandler.TemporaryFileUploadHandler',
)
#! /usr/bin/env python
import sys
import json
import random
import copy
from collections import defaultdict
from argparse import ArgumentParser, FileType
from datetime import datetime
def generate_user(user_number):
return {
"pk": user_number,
"model": "auth.user",
"fields": {
"status": "w",
"last_name": "Last",
"gold": 0,
"is_staff": False,
"user_permissions": [],
"interesting_tags": "",
"email_key": None,
"date_joined": "2012-04-26 11:36:39",
"first_name": "",
"email_isvalid": False,
"avatar_type": "n",
"website": "",
"is_superuser": False,
"date_of_birth": None,
"last_login": "2012-04-26 11:36:48",
"location": "",
"new_response_count": 0,
"email": "user{num}@example.com".format(num=user_number),
"username": "user{num}".format(num=user_number),
"is_active": True,
"consecutive_days_visit_count": 0,
"email_tag_filter_strategy": 1,
"groups": [],
"password": "sha1$90e6f$562a1d783a0c47ce06ebf96b8c58123a0671bbf0",
"silver": 0,
"bronze": 0,
"questions_per_page": 10,
"about": "",
"show_country": True,
"country": "",
"display_tag_filter_strategy": 0,
"seen_response_count": 0,
"real_name": "",
"ignored_tags": "",
"reputation": 1,
"gravatar": "366d981a10116969c568a18ee090f44c",
"last_seen": "2012-04-26 11:36:39"
}
}
def parse_args(args=sys.argv[1:]):
parser = ArgumentParser()
parser.add_argument('-d', '--data', type=FileType('r'), default=sys.stdin)
parser.add_argument('-o', '--output', type=FileType('w'), default=sys.stdout)
parser.add_argument('count', type=int)
return parser.parse_args(args)
def main(args=sys.argv[1:]):
args = parse_args(args)
data = json.load(args.data)
unique_students = set(entry['fields']['student'] for entry in data)
if args.count > len(unique_students) * 0.1:
raise Exception("Can't be sufficiently anonymous selecting {count} of {unique} students".format(
count=args.count, unique=len(unique_students)))
by_problems = defaultdict(list)
for entry in data:
by_problems[entry['fields']['module_id']].append(entry)
out_data = []
out_pk = 1
for name, answers in by_problems.items():
for student_id in xrange(args.count):
sample = random.choice(answers)
data = copy.deepcopy(sample)
data["fields"]["student"] = student_id + 1
data["fields"]["created"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
data["fields"]["modified"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
data["pk"] = out_pk
out_pk += 1
out_data.append(data)
for student_id in xrange(args.count):
out_data.append(generate_user(student_id))
json.dump(out_data, args.output, indent=2)
if __name__ == "__main__":
sys.exit(main())
#!/usr/bin/python
from loncapa_check import *
#!/usr/bin/python
#
# File: mitx/lib/loncapa/loncapa_check.py
#
# Python functions which duplicate the standard comparison functions available to LON-CAPA problems.
# Used in translating LON-CAPA problems to i4x problem specification language.
import random
def lc_random(lower,upper,stepsize):
'''
like random.randrange but lower and upper can be non-integer
'''
nstep = int((upper-lower)/(1.0*stepsize))
choices = [lower+x*stepsize for x in range(nstep)]
return random.choice(choices)
......@@ -27,12 +27,16 @@ def render_to_string(template_name, dictionary, context=None, namespace='main'):
# collapse context_instance to a single dictionary for mako
context_dictionary = {}
context_instance['settings'] = settings
context_instance['MITX_ROOT_URL'] = settings.MITX_ROOT_URL
for d in mitxmako.middleware.requestcontext:
context_dictionary.update(d)
for d in context_instance:
context_dictionary.update(d)
if context:
context_dictionary.update(context)
## HACK
## We should remove this, and possible set COURSE_TITLE in the middleware from the session.
if 'COURSE_TITLE' not in context_dictionary: context_dictionary['COURSE_TITLE'] = ''
# fetch and render template
template = middleware.lookup[namespace].get_template(template_name)
return template.render(**context_dictionary)
......
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# File: sympy_check2.py
# Date: 02-May-12
# Author: I. Chuang <ichuang@mit.edu>
#
# Use sympy to check for expression equality
#
# Takes in math expressions given as Presentation MathML (from ASCIIMathML), converts to Content MathML using SnuggleTeX
import os, sys, string, re
import traceback
from formula import *
#-----------------------------------------------------------------------------
# check function interface
def sympy_check(expect,ans,adict={},symtab=None,extra_options=None):
options = {'__MATRIX__':False,'__ABC__':False,'__LOWER__':False}
if extra_options: options.update(extra_options)
for op in options: # find options in expect string
if op in expect:
expect = expect.replace(op,'')
options[op] = True
expect = expect.replace('__OR__','__or__') # backwards compatibility
if options['__LOWER__']:
expect = expect.lower()
ans = ans.lower()
try:
ret = check(expect,ans,
matrix=options['__MATRIX__'],
abcsym=options['__ABC__'],
symtab=symtab,
)
except Exception, err:
return {'ok': False,
'msg': 'Error %s<br/>Failed in evaluating check(%s,%s)' % (err,expect,ans)
}
return ret
#-----------------------------------------------------------------------------
# pretty generic checking function
def check(expect,given,numerical=False,matrix=False,normphase=False,abcsym=False,do_qubit=True,symtab=None,dosimplify=False):
"""
Returns dict with
'ok': True if check is good, False otherwise
'msg': response message (in HTML)
"expect" may have multiple possible acceptable answers, separated by "__OR__"
"""
if "__or__" in expect: # if multiple acceptable answers
eset = expect.split('__or__') # then see if any match
for eone in eset:
ret = check(eone,given,numerical,matrix,normphase,abcsym,do_qubit,symtab,dosimplify)
if ret['ok']:
return ret
return ret
flags = {}
if "__autonorm__" in expect:
flags['autonorm']=True
expect = expect.replace('__autonorm__','')
matrix = True
threshold = 1.0e-3
if "__threshold__" in expect:
(expect,st) = expect.split('__threshold__')
threshold = float(st)
numerical=True
if str(given)=='' and not (str(expect)==''):
return {'ok': False, 'msg': ''}
try:
xgiven = my_sympify(given,normphase,matrix,do_qubit=do_qubit,abcsym=abcsym,symtab=symtab)
except Exception,err:
return {'ok': False,'msg': 'Error %s<br/> in evaluating your expression "%s"' % (err,given)}
try:
xexpect = my_sympify(expect,normphase,matrix,do_qubit=do_qubit,abcsym=abcsym,symtab=symtab)
except Exception,err:
return {'ok': False,'msg': 'Error %s<br/> in evaluating OUR expression "%s"' % (err,expect)}
if 'autonorm' in flags: # normalize trace of matrices
try:
xgiven /= xgiven.trace()
except Exception, err:
return {'ok': False,'msg': 'Error %s<br/> in normalizing trace of your expression %s' % (err,to_latex(xgiven))}
try:
xexpect /= xexpect.trace()
except Exception, err:
return {'ok': False,'msg': 'Error %s<br/> in normalizing trace of OUR expression %s' % (err,to_latex(xexpect))}
msg = 'Your expression was evaluated as ' + to_latex(xgiven)
# msg += '<br/>Expected ' + to_latex(xexpect)
# msg += "<br/>flags=%s" % flags
if matrix and numerical:
xgiven = my_evalf(xgiven,chop=True)
dm = my_evalf(sympy.Matrix(xexpect)-sympy.Matrix(xgiven),chop=True)
msg += " = " + to_latex(xgiven)
if abs(dm.vec().norm().evalf())<threshold:
return {'ok': True,'msg': msg}
else:
pass
#msg += "dm = " + to_latex(dm) + " diff = " + str(abs(dm.vec().norm().evalf()))
#msg += "expect = " + to_latex(xexpect)
elif dosimplify:
if (sympy.simplify(xexpect)==sympy.simplify(xgiven)):
return {'ok': True,'msg': msg}
elif numerical:
if (abs((xexpect-xgiven).evalf(chop=True))<threshold):
return {'ok': True,'msg': msg}
elif (xexpect==xgiven):
return {'ok': True,'msg': msg}
#msg += "<p/>expect='%s', given='%s'" % (expect,given) # debugging
# msg += "<p/> dot test " + to_latex(dot(sympy.Symbol('x'),sympy.Symbol('y')))
return {'ok': False,'msg': msg }
#-----------------------------------------------------------------------------
# Check function interface, which takes pmathml input
def sympy_check2(expect,ans,adict={},abname=''):
msg = ''
# msg += '<p/>abname=%s' % abname
# msg += '<p/>adict=%s' % (repr(adict).replace('<','&lt;'))
threshold = 1.0e-3
DEBUG = True
# parse expected answer
try:
fexpect = my_sympify(str(expect))
except Exception,err:
msg += '<p>Error %s in parsing OUR expected answer "%s"</p>' % (err,expect)
return {'ok':False,'msg':msg}
# if expected answer is a number, try parsing provided answer as a number also
try:
fans = my_sympify(str(ans))
except Exception,err:
fans = None
if fexpect.is_number and fans and fans.is_number:
if abs(abs(fans-fexpect)/fexpect)<threshold:
return {'ok':True,'msg':msg}
else:
msg += '<p>You entered: %s</p>' % to_latex(fans)
return {'ok':False,'msg':msg}
if fexpect==fans:
msg += '<p>You entered: %s</p>' % to_latex(fans)
return {'ok':True,'msg':msg}
# convert mathml answer to formula
mmlbox = abname+'_fromjs'
if mmlbox in adict:
mmlans = adict[mmlbox]
f = formula(mmlans)
# get sympy representation of the formula
# if DEBUG: msg += '<p/> mmlans=%s' % repr(mmlans).replace('<','&lt;')
try:
fsym = f.sympy
msg += '<p>You entered: %s</p>' % to_latex(f.sympy)
except Exception,err:
msg += "<p>Error %s in converting to sympy</p>" % str(err).replace('<','&lt;')
if DEBUG: msg += "<p><pre>%s</pre></p>" % traceback.format_exc()
return {'ok':False,'msg':msg}
# compare with expected
if fexpect.is_number:
if fsym.is_number:
if abs(abs(fsym-fexpect)/fexpect)<threshold:
return {'ok':True,'msg':msg}
return {'ok':False,'msg':msg}
msg += "<p>Expecting a numerical answer!</p>"
msg += "<p>given = %s</p>" % repr(ans)
msg += "<p>fsym = %s</p>" % repr(fsym)
# msg += "<p>cmathml = <pre>%s</pre></p>" % str(f.cmathml).replace('<','&lt;')
return {'ok':False,'msg':msg}
if fexpect==fsym:
return {'ok':True,'msg':msg}
if type(fexpect)==list:
try:
xgiven = my_evalf(fsym,chop=True)
dm = my_evalf(sympy.Matrix(fexpect)-sympy.Matrix(xgiven),chop=True)
if abs(dm.vec().norm().evalf())<threshold:
return {'ok': True,'msg': msg}
except Exception,err:
msg += "<p>Error %s in comparing expected (a list) and your answer</p>" % str(err).replace('<','&lt;')
if DEBUG: msg += "<p/><pre>%s</pre>" % traceback.format_exc()
return {'ok':False,'msg':msg}
#diff = (fexpect-fsym).simplify()
#fsym = fsym.simplify()
#fexpect = fexpect.simplify()
try:
diff = (fexpect-fsym)
except Exception,err:
diff = None
if DEBUG:
msg += "<p>Got: %s</p>" % repr(fsym)
# msg += "<p/>Got: %s" % str([type(x) for x in fsym.atoms()]).replace('<','&lt;')
msg += "<p>Expecting: %s</p>" % repr(fexpect).replace('**','^').replace('hat(I)','hat(i)')
# msg += "<p/>Expecting: %s" % str([type(x) for x in fexpect.atoms()]).replace('<','&lt;')
if diff:
msg += "<p>Difference: %s</p>" % to_latex(diff)
return {'ok':False,'msg':msg,'ex':fexpect,'got':fsym}
def sctest1():
x = "1/2*(1+(k_e* Q* q)/(m *g *h^2))"
y = '''
<math xmlns="http://www.w3.org/1998/Math/MathML">
<mstyle displaystyle="true">
<mfrac>
<mn>1</mn>
<mn>2</mn>
</mfrac>
<mrow>
<mo>(</mo>
<mn>1</mn>
<mo>+</mo>
<mfrac>
<mrow>
<msub>
<mi>k</mi>
<mi>e</mi>
</msub>
<mo>⋅</mo>
<mi>Q</mi>
<mo>⋅</mo>
<mi>q</mi>
</mrow>
<mrow>
<mi>m</mi>
<mo>⋅</mo>
<mrow>
<mi>g</mi>
<mo>⋅</mo>
</mrow>
<msup>
<mi>h</mi>
<mn>2</mn>
</msup>
</mrow>
</mfrac>
<mo>)</mo>
</mrow>
</mstyle>
</math>
'''.strip()
z = "1/2(1+(k_e* Q* q)/(m *g *h^2))"
r = sympy_check2(x,z,{'a':z,'a_fromjs':y},'a')
return r
......@@ -3,7 +3,6 @@ import json
import sys
from django.conf import settings
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
......@@ -60,7 +59,10 @@ def send_feedback(request):
def info(request):
''' Info page (link from main header) '''
if not request.user.is_authenticated():
return redirect('/')
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)
......@@ -4,14 +4,15 @@ require 'tempfile'
# Build Constants
REPO_ROOT = File.dirname(__FILE__)
BUILD_DIR = File.join(REPO_ROOT, "build")
REPORT_DIR = File.join(REPO_ROOT, "reports")
# Packaging constants
DEPLOY_DIR = "/opt/wwc"
PACKAGE_NAME = "mitx"
LINK_PATH = "/opt/wwc/mitx"
VERSION = "0.1"
PKG_VERSION = "0.1"
COMMIT = (ENV["GIT_COMMIT"] || `git rev-parse HEAD`).chomp()[0, 10]
BRANCH = (ENV["GIT_BRANCH"] || `git symbolic-ref -q HEAD`).chomp().gsub('refs/heads/', '').gsub('origin/', '').gsub('/', '_').downcase()
BRANCH = (ENV["GIT_BRANCH"] || `git symbolic-ref -q HEAD`).chomp().gsub('refs/heads/', '').gsub('origin/', '')
BUILD_NUMBER = (ENV["BUILD_NUMBER"] || "dev").chomp()
if BRANCH == "master"
......@@ -19,14 +20,41 @@ if BRANCH == "master"
else
DEPLOY_NAME = "#{PACKAGE_NAME}-#{BRANCH}-#{BUILD_NUMBER}-#{COMMIT}"
end
INSTALL_DIR_PATH = File.join(DEPLOY_DIR, DEPLOY_NAME)
PACKAGE_REPO = "packages@gp.mitx.mit.edu:/opt/pkgrepo.incoming"
NORMALIZED_DEPLOY_NAME = DEPLOY_NAME.downcase().gsub(/[_\/]/, '-')
INSTALL_DIR_PATH = File.join(DEPLOY_DIR, NORMALIZED_DEPLOY_NAME)
# Set up the clean and clobber tasks
CLOBBER.include('build')
CLOBBER.include(BUILD_DIR, REPORT_DIR, 'cover*', '.coverage')
CLEAN.include("#{BUILD_DIR}/*.deb", "#{BUILD_DIR}/util")
def select_executable(*cmds)
cmds.find_all{ |cmd| system("which #{cmd} > /dev/null 2>&1") }[0] || fail("No executables found from #{cmds.join(', ')}")
end
task :default => [:pep8, :pylint, :test]
directory REPORT_DIR
task :pep8 => REPORT_DIR do
sh("pep8 --ignore=E501 djangoapps | tee #{REPORT_DIR}/pep8.report")
end
task :pylint => REPORT_DIR do
Dir.chdir("djangoapps") do
Dir["*"].each do |app|
sh("pylint -f parseable #{app} | tee #{REPORT_DIR}/#{app}.pylint.report")
end
end
end
task :test => REPORT_DIR do
ENV['NOSE_XUNIT_FILE'] = File.join(REPORT_DIR, "nosetests.xml")
django_admin = ENV['DJANGO_ADMIN_PATH'] || select_executable('django-admin.py', 'django-admin')
sh("#{django_admin} test --settings=envs.test --pythonpath=. $(ls djangoapps)")
end
task :package do
FileUtils.mkdir_p(BUILD_DIR)
......@@ -53,10 +81,15 @@ task :package do
args = ["fakeroot", "fpm", "-s", "dir", "-t", "deb",
"--after-install=#{postinstall.path}",
"--prefix=#{INSTALL_DIR_PATH}",
"--exclude=build",
"--exclude=rakefile",
"--exclude=.git",
"--exclude=**/*.pyc",
"--exclude=reports",
"-C", "#{REPO_ROOT}",
"--provides=#{PACKAGE_NAME}",
"--name=#{DEPLOY_NAME}",
"--version=#{VERSION}",
"--name=#{NORMALIZED_DEPLOY_NAME}",
"--version=#{PKG_VERSION}",
"-a", "all",
"."]
system(*args) || raise("fpm failed to build the .deb")
......@@ -64,5 +97,5 @@ task :package do
end
task :publish => :package do
sh("scp #{BUILD_DIR}/#{DEPLOY_NAME}_#{VERSION}-1_all.deb #{PACKAGE_REPO}")
sh("scp #{BUILD_DIR}/#{NORMALIZED_DEPLOY_NAME}_#{PKG_VERSION}*.deb #{PACKAGE_REPO}")
end
......@@ -2,10 +2,10 @@ section.help.main-content {
padding: lh();
h1 {
margin-top: 0;
border-bottom: 1px solid #ddd;
margin-bottom: lh();
margin-top: 0;
padding-bottom: lh();
border-bottom: 1px solid #ddd;
}
p {
......@@ -17,9 +17,9 @@ section.help.main-content {
}
section.self-help {
float: left;
margin-bottom: lh();
margin-right: flex-gutter();
float: left;
width: flex-grid(6);
ul {
......@@ -36,17 +36,17 @@ section.help.main-content {
width: flex-grid(6);
dl {
margin-bottom: lh();
display: block;
margin-bottom: lh();
dd {
margin-bottom: lh();
}
dt {
font-weight: bold;
float: left;
clear: left;
float: left;
font-weight: bold;
width: flex-grid(2, 6);
}
}
......
......@@ -16,28 +16,28 @@ div.info-wrapper {
list-style: none;
> li {
padding-bottom: lh(.5);
margin-bottom: lh(.5);
@extend .clearfix;
border-bottom: 1px solid #e3e3e3;
margin-bottom: lh(.5);
padding-bottom: lh(.5);
&:first-child {
padding: lh(.5);
margin: 0 (-(lh(.5))) lh();
background: $cream;
border-bottom: 1px solid darken($cream, 10%);
margin: 0 (-(lh(.5))) lh();
padding: lh(.5);
}
h2 {
float: left;
width: flex-grid(2, 9);
margin: 0 flex-gutter() 0 0;
width: flex-grid(2, 9);
}
section.update-description {
float: left;
width: flex-grid(7, 9);
margin-bottom: 0;
width: flex-grid(7, 9);
li {
margin-bottom: lh(.5);
......@@ -55,9 +55,9 @@ div.info-wrapper {
section.handouts {
@extend .sidebar;
border-left: 1px solid #d3d3d3;
@include border-radius(0 4px 4px 0);
border-right: 0;
border-left: 1px solid #d3d3d3;
header {
@extend .bottom-border;
......@@ -69,32 +69,32 @@ div.info-wrapper {
}
p {
color: #666;
font-size: 12px;
margin-bottom: 0;
margin-top: 4px;
font-size: 12px;
color: #666;
}
}
ol {
list-style: none;
background: none;
list-style: none;
li {
@include box-shadow(0 1px 0 #eee);
@extend .clearfix;
background: none;
border-bottom: 1px solid #d3d3d3;
@include box-shadow(0 1px 0 #eee);
@include box-sizing(border-box);
@extend .clearfix;
padding: 7px lh(.75);
background: none;
position: relative;
&.expandable,
&.collapsable {
h4 {
padding-left: 18px;
font-style: $body-font-size;
font-weight: normal;
padding-left: 18px;
}
}
......@@ -103,10 +103,10 @@ div.info-wrapper {
margin: 7px (-(lh(.75))) 0;
li {
padding-left: 18px + lh(.75);
@include box-shadow(inset 0 1px 0 #eee);
border-top: 1px solid #d3d3d3;
border-bottom: 0;
border-top: 1px solid #d3d3d3;
@include box-shadow(inset 0 1px 0 #eee);
padding-left: 18px + lh(.75);
}
}
......@@ -116,13 +116,13 @@ div.info-wrapper {
div.hitarea {
background-image: url('../images/treeview-default.gif');
width: 100%;
height: 100%;
max-height: 20px;
display: block;
position: absolute;
height: 100%;
left: lh(.75);
margin-left: 0;
max-height: 20px;
position: absolute;
width: 100%;
&:hover {
opacity: 0.6;
......@@ -140,27 +140,27 @@ div.info-wrapper {
h3 {
border-bottom: 0;
text-transform: uppercase;
font-weight: bold;
color: #999;
@include box-shadow(none);
color: #999;
font-size: 12px;
font-weight: bold;
text-transform: uppercase;
}
p {
font-size: $body-font-size;
letter-spacing: 0;
margin: 0;
text-transform: none;
letter-spacing: 0;
font-size: $body-font-size;
a {
padding-right: 8px;
&:before {
color: #ccc;
content: "•";
@include inline-block();
padding-right: 8px;
color: #ccc;
}
&:first-child {
......@@ -173,10 +173,10 @@ div.info-wrapper {
}
a {
@include transition();
color: lighten($text-color, 10%);
text-decoration: none;
@include inline-block();
text-decoration: none;
@include transition();
&:hover {
color: $mit-red;
......
......@@ -4,14 +4,14 @@ div.profile-wrapper {
section.user-info {
@extend .sidebar;
@include border-radius(0px 4px 4px 0);
border-left: 1px solid #d3d3d3;
@include border-radius(0px 4px 4px 0);
border-right: 0;
header {
padding: lh(.5) lh();
margin: 0 ;
@extend .bottom-border;
margin: 0 ;
padding: lh(.5) lh();
h1 {
font-size: 18px;
......@@ -20,12 +20,12 @@ div.profile-wrapper {
}
a {
color: #999;
font-size: 12px;
position: absolute;
top: 13px;
right: lh(.5);
text-transform: uppercase;
font-size: 12px;
color: #999;
top: 13px;
&:hover {
color: #555;
......@@ -37,14 +37,14 @@ div.profile-wrapper {
list-style: none;
li {
@include transition();
border-bottom: 1px solid #d3d3d3;
@include box-shadow(0 1px 0 #eee);
color: lighten($text-color, 10%);
display: block;
text-decoration: none;
@include box-shadow(0 1px 0 #eee);
padding: 7px lh();
border-bottom: 1px solid #d3d3d3;
position: relative;
text-decoration: none;
@include transition();
div#location_sub, div#language_sub {
font-weight: bold;
......@@ -57,9 +57,9 @@ div.profile-wrapper {
input {
&[type="text"] {
@include box-sizing(border-box);
margin: lh(.5) 0;
width: 100%;
@include box-sizing(border-box);
}
&[type="input"]{
......@@ -80,12 +80,12 @@ div.profile-wrapper {
a.edit-email,
a.name-edit,
a.email-edit {
color: #999;
font-size: 12px;
position: absolute;
top: 9px;
right: lh(.5);
text-transform: uppercase;
font-size: 12px;
color: #999;
top: 9px;
&:hover {
color: #555;
......@@ -93,10 +93,10 @@ div.profile-wrapper {
}
p {
color: #999;
font-size: 12px;
margin-bottom: 0;
margin-top: 4px;
color: #999;
}
a.deactivate {
......@@ -132,10 +132,10 @@ div.profile-wrapper {
padding: 7px lh();
h2 {
margin-top: 0;
font-size: $body-font-size;
font-weight: bold;
margin-top: 0;
text-transform: uppercase;
font-size: $body-font-size;
}
}
}
......@@ -148,14 +148,14 @@ div.profile-wrapper {
@extend .clearfix;
h1 {
margin: 0;
float: left;
margin: 0;
}
}
div#grade-detail-graph {
width: 100%;
min-height: 300px;
width: 100%;
}
> ol {
......
......@@ -3,8 +3,8 @@ div.book-wrapper {
section.book-sidebar {
@extend .sidebar;
@include box-sizing(border-box);
@extend .tran;
@include box-sizing(border-box);
ul#booknav {
font-size: 12px;
......@@ -22,14 +22,14 @@ div.book-wrapper {
padding-left: 30px;
div.hitarea {
margin-left: -22px;
background-image: url('../images/treeview-default.gif');
margin-left: -22px;
position: relative;
top: 4px;
&:hover {
opacity: 0.6;
filter: alpha(opacity=60);
opacity: 0.6;
}
}
......@@ -63,13 +63,13 @@ div.book-wrapper {
li {
&.last {
float: left;
display: block;
float: left;
a {
@include box-shadow(inset -1px 0 0 lighten(#f6efd4, 5%));
border-right: 1px solid darken(#f6efd4, 20%);
border-left: 0;
border-right: 1px solid darken(#f6efd4, 20%);
@include box-shadow(inset -1px 0 0 lighten(#f6efd4, 5%));
}
}
......@@ -81,10 +81,10 @@ div.book-wrapper {
}
&.bottom-nav {
margin-top: lh();
margin-bottom: -(lh());
border-bottom: 0;
border-top: 1px solid #EDDFAA;
margin-bottom: -(lh());
margin-top: lh();
}
}
......@@ -110,18 +110,18 @@ div.book-wrapper {
}
h2 {
padding: 0;
visibility: hidden;
width: 10px;
padding: 0;
}
}
ul#booknav {
max-height: 100px;
overflow: hidden;
padding: 0;
visibility: hidden;
width: 10px;
padding: 0;
overflow: hidden;
max-height: 100px;
}
}
......
......@@ -35,10 +35,10 @@ img {
}
#{$all-text-inputs}, textarea {
@include box-shadow(0 -1px 0 #fff);
@include linear-gradient(#eee, #fff);
border: 1px solid #999;
@include box-shadow(0 -1px 0 #fff);
font: $body-font-size $body-font-family;
@include linear-gradient(#eee, #fff);
padding: 4px;
&:focus {
......@@ -63,7 +63,7 @@ a {
p &, li > &, span > &, .inline {
border-bottom: 1px solid #bbb;
font-style: italic;
// font-style: italic;
}
&:hover, &:focus {
......
.clearfix:after {
clear: both;
content: ".";
display: block;
height: 0;
clear: both;
visibility: hidden;
}
......@@ -40,27 +40,27 @@ h1.top-header {
-webkit-font-smoothing: antialiased;
&:hover, &:focus {
border: 1px solid darken(#888, 20%);
@include box-shadow(inset 0 1px 0 lighten(#888, 20%), 0 0 3px #ccc);
@include linear-gradient(lighten(#888, 10%), darken(#888, 5%));
border: 1px solid darken(#888, 20%);
}
}
.light-button, a.light-button {
@include box-shadow(inset 0 1px 0 #fff);
@include linear-gradient(#fff, lighten(#888, 40%));
@include border-radius(3px);
border: 1px solid #ccc;
padding: 4px 8px;
@include border-radius(3px);
@include box-shadow(inset 0 1px 0 #fff);
color: #666;
cursor: pointer;
font: normal $body-font-size $body-font-family;
@include linear-gradient(#fff, lighten(#888, 40%));
padding: 4px 8px;
text-decoration: none;
cursor: pointer;
-webkit-font-smoothing: antialiased;
&:hover, &:focus {
@include linear-gradient(#fff, lighten(#888, 37%));
border: 1px solid #ccc;
@include linear-gradient(#fff, lighten(#888, 37%));
text-decoration: none;
}
}
......@@ -70,8 +70,8 @@ h1.top-header {
color: $mit-red;
&:hover {
text-decoration: none;
color: darken($mit-red, 20%);
text-decoration: none;
}
}
}
......@@ -110,13 +110,13 @@ h1.top-header {
}
a {
font-style: normal;
border: none;
font-style: normal;
}
.bottom-border {
@include box-shadow(0 1px 0 #eee);
border-bottom: 1px solid #d3d3d3;
@include box-shadow(0 1px 0 #eee);
}
@media print {
......@@ -124,10 +124,10 @@ h1.top-header {
}
h3 {
border: none;
border-bottom: 1px solid #d3d3d3;
@extend .bottom-border;
background: none;
border: none;
border-bottom: 1px solid #d3d3d3;
color: #000;
font-weight: normal;
margin: 0;
......@@ -172,8 +172,8 @@ h1.top-header {
position: relative;
h2 {
padding-right: 20px;
margin: 0;
padding-right: 20px;
}
a {
......@@ -205,10 +205,10 @@ h1.top-header {
border-bottom: 1px solid darken($cream, 10%);
@include box-shadow(inset 0 1px 0 #fff, inset 1px 0 0 #fff);
font-size: 12px;
height:46px;
line-height: 46px;
margin: (-$body-line-height) (-$body-line-height) $body-line-height;
text-shadow: 0 1px 0 #fff;
line-height: 46px;
height:46px;
@media print {
display: none;
......@@ -242,10 +242,10 @@ h1.top-header {
}
p.ie-warning {
background: yellow;
display: block !important;
line-height: 1.3em;
background: yellow;
margin-bottom: 0;
padding: lh();
text-align: left;
margin-bottom: 0;
}
// Flexible grid
@function flex-grid($columns, $container-columns: $fg-max-columns) {
$width: $columns * $fg-column + ($columns - 1) * $fg-gutter;
$container-width: $container-columns * $fg-column + ($container-columns - 1) * $fg-gutter;
@return percentage($width / $container-width);
}
// Flexible grid gutter
@function flex-gutter($container-columns: $fg-max-columns, $gutter: $fg-gutter) {
$container-width: $container-columns * $fg-column + ($container-columns - 1) * $fg-gutter;
@return percentage($gutter / $container-width);
}
// Percentage of container calculator
@function perc($width, $container-width: $max-width) {
@return percentage($width / $container-width);
}
// Line-height
@function lh($amount: 1) {
@return $body-line-height * $amount;
......
// Variables
// ---------------------------------------- //
// fonts
$body-font-family: "Open Sans", "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Geneva, Verdana, sans-serif;;
// Type
$body-font-family: "Open Sans", "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Geneva, Verdana, sans-serif;
$body-font-size: 14px;
// grid
$columns: 12;
$column-width: 80px;
$gutter-width: 25px;
$max-width: ($columns * $column-width) + (($columns - 1) * $gutter-width);
$gw-column: perc($column-width);
$gw-gutter: perc($gutter-width);
$body-line-height: golden-ratio($body-font-size, 1);
//Flexible grid
$fg-column: $column-width;
$fg-gutter: $gutter-width;
$fg-max-columns: $columns;
// Grid
$fg-column: 80px;
$fg-gutter: 25px;
$fg-max-columns: 12;
$fg-max-width: 1400px;
$fg-min-width: 810px;
// color
// Color
$light-gray: #ddd;
$dark-gray: #333;
$mit-red: #993333;
$cream: #F6EFD4;
$text-color: $dark-gray;
$border-color: $light-gray;
// JM MOSFET AMPLIFIER
div#graph-container {
section.tool-wrapper {
@extend .clearfix;
border-top: 1px solid #ddd;
padding-top: lh(1.0);
canvas#graph {
width: flex-grid(4.5, 9);
float: left;
margin-right: flex-gutter(9);
}
div.graph-controls {
width: flex-grid(4.5, 9);
float: left;
select#musicTypeSelect {
display: block;
margin-bottom: lh();
background: #073642;
border-bottom: 1px solid darken(#002b36, 10%);
border-top: 1px solid darken(#002b36, 10%);
@include box-shadow(inset 0 0 0 4px darken(#094959, 2%));
color: #839496;
display: table;
margin: lh() (-(lh())) 0;
div#graph-container {
background: none;
@include box-sizing(border-box);
display: table-cell;
padding: lh();
vertical-align: top;
width: flex-grid(4.5, 9) + flex-gutter(9);
.ui-widget-content {
background: none;
border: none;
@include border-radius(0);
}
div#graph-output {
display: block;
margin-bottom: lh();
canvas {
width: 100%;
}
div#graph-listen {
display: block;
margin-bottom: lh();
}
p {
margin-bottom: lh(.5);
}
div#label {
display: inline-block;
}
input#playButton {
display: block;
ul.ui-tabs-nav {
background: darken(#073642, 2%);
border-bottom: 1px solid darken(#073642, 8%);
@include border-radius(0);
margin: (-(lh())) (-(lh())) 0;
padding: 0;
position: relative;
width: 110%;
li {
background: none;
border: none;
@include border-radius(0);
color: #fff;
margin-bottom: 0;
&.ui-tabs-selected {
background-color: #073642;
border-left: 1px solid darken(#073642, 8%);
border-right: 1px solid darken(#073642, 8%);
&:first-child {
border-left: none;
}
a {
color: #eee8d5;
}
}
a {
border: none;
color: #839496;
font: bold 12px $body-font-family;
letter-spacing: 1px;
text-transform: uppercase;
&:hover {
color: #eee8d5;
}
}
}
}
}
}
div#schematic-container {
@extend .clearfix;
canvas {
div#controlls-container {
@extend .clearfix;
background: darken(#073642, 2%);
border-right: 1px solid darken(#002b36, 6%);
@include box-shadow(1px 0 0 lighten(#002b36, 6%), inset 0 0 0 4px darken(#094959, 6%));
@include box-sizing(border-box);
display: table-cell;
padding: lh();
vertical-align: top;
width: flex-grid(4.5, 9);
float: left;
margin-right: flex-gutter(9);
}
div.schematic-sliders {
width: flex-grid(4.5, 9);
float: left;
div.slider-label#vs {
margin-top: lh(2.0);
div.graph-controls {
div.music-wrapper {
@extend .clearfix;
border-bottom: 1px solid darken(#073642, 10%);
@include box-shadow(0 1px 0 lighten(#073642, 2%));
margin-bottom: lh();
padding: 0 0 lh();
input#playButton {
border-color: darken(#002b36, 6%);
@include button(simple, lighten( #586e75, 5% ));
display: block;
float: right;
font: bold 14px $body-font-family;
&:active {
@include box-shadow(none);
}
&[value="Stop"] {
@include button(simple, darken(#268bd2, 30%));
font: bold 14px $body-font-family;
&:active {
@include box-shadow(none);
}
}
}
}
div.inputs-wrapper {
@extend .clearfix;
border-bottom: 1px solid darken(#073642, 10%);
@include box-shadow(0 1px 0 lighten(#073642, 2%));
@include clearfix;
margin-bottom: lh();
margin-bottom: lh();
padding: 0 0 lh();
}
p {
font-weight: bold;
@include inline-block();
margin: 0;
text-shadow: 0 -1px 0 darken(#073642, 10%);
-webkit-font-smoothing: antialiased;
}
ul {
@include inline-block();
margin-bottom: 0;
li {
@include inline-block();
margin-bottom: 0;
input {
margin-right: 5px;
}
}
}
div#graph-listen {
display: block;
float: left;
margin-bottom: 0;
margin-right: 20px;
margin-top: 8px;
text-align: right;
}
}
div.slider-label {
margin-bottom: lh(0.5);
label {
@include border-radius(2px);
color: #fff;
font-weight: bold;
padding: 3px;
-webkit-font-smoothing: antialiased;
}
div.slider {
margin-bottom: lh(1);
//MOSFET AMPLIFIER
label[for="vinCheckbox"], label[for="vinRadioButton"]{
color: desaturate(#00bfff, 50%);
}
}
}
//End JM MOSFET AMPLIFIER
// Labels
div.graph-controls, div#graph-listen {
label {
@include border-radius(2px);
font-weight: bold;
padding: 3px;
}
//MOSFET AMPLIFIER
label[for="vinCheckbox"], label[for="vinRadioButton"]{
color: desaturate(#00bfff, 50%);
}
label[for="voutCheckbox"], label[for="voutRadioButton"]{
label[for="voutCheckbox"], label[for="voutRadioButton"]{
color: darken(#ffcf48, 20%);
}
label[for="vrCheckbox"], label[for="vrRadioButton"]{
}
label[for="vrCheckbox"], label[for="vrRadioButton"]{
color: desaturate(#1df914, 40%);
}
//RC Filters
label[for="vcCheckbox"], label[for="vcRadioButton"]{
}
//RC Filters
label[for="vcCheckbox"], label[for="vcRadioButton"]{
color: darken(#ffcf48, 20%);
}
//RLC Series
label[for="vlCheckbox"], label[for="vlRadioButton"]{
}
//RLC Series
label[for="vlCheckbox"], label[for="vlRadioButton"]{
color: desaturate(#d33682, 40%);
}
div.schematic-sliders {
div.top-sliders {
@extend .clearfix;
border-bottom: 1px solid darken(#073642, 10%);
@include box-shadow(0 1px 0 lighten(#073642, 2%));
margin-bottom: lh();
padding: 0 0 lh();
select#musicTypeSelect {
font: 16px $body-font-family;
@include inline-block();
margin-bottom: 0;
}
p {
font-weight: bold;
@include inline-block();
margin: 0 lh(.5) lh() 0;
text-shadow: 0 -1px 0 darken(#073642, 10%);
-webkit-font-smoothing: antialiased;
}
}
div.slider-label {
font-weight: bold;
margin-bottom: lh(0.5);
text-shadow: 0 -1px 0 darken(#073642, 10%);
-webkit-font-smoothing: antialiased;
}
div.slider {
margin-bottom: lh(1);
&.ui-slider-horizontal {
background: darken(#002b36, 2%);
border: 1px solid darken(#002b36, 8%);
@include box-shadow(none);
height: 0.4em;
}
.ui-slider-handle {
background: lighten( #586e75, 5% ) url('/static/images/amplifier-slider-handle.png') center no-repeat;
border: 1px solid darken(#002b36, 8%);
@include box-shadow(inset 0 1px 0 lighten( #586e75, 20% ));
margin-top: -.3em;
&:hover, &:active {
background-color: lighten( #586e75, 10% );
}
}
}
}
}
}
html {
height: 100%;
max-height: 100%;
}
body.courseware {
height: 100%;
max-height: 100%;
}
div.course-wrapper {
@extend .table-wrapper;
......@@ -7,9 +17,12 @@ div.course-wrapper {
section.course-content {
@extend .content;
overflow: hidden;
@include border-top-right-radius(4px);
@include border-bottom-right-radius(4px);
h1 {
@extend .top-header;
margin: 0 0 lh();
}
p {
......@@ -159,18 +172,71 @@ div.course-wrapper {
margin-bottom: 15px;
padding: 0 0 15px;
header {
@extend h1.top-header;
margin-bottom: -16px;
h1 {
margin: 0;
}
h2 {
float: right;
margin-right: 0;
margin-top: 8px;
text-align: right;
padding-right: 0;
}
}
&:last-child {
border-bottom: none;
margin-bottom: 0;
padding-bottom: 0;
}
ul {
list-style: disc outside none;
padding-left: 1em;
}
nav.sequence-bottom {
ul {
list-style: none;
padding: 0;
}
}
}
}
section.tutorials {
h2 {
margin-bottom: lh();
}
ul {
list-style: disc outside none;
margin-left: lh();
margin: 0;
@include clearfix();
li {
width: flex-grid(3, 9);
float: left;
margin-right: flex-gutter(9);
margin-bottom: lh();
&:nth-child(3n) {
margin-right: 0;
}
&:nth-child(3n+1) {
clear: both;
}
a {
font-weight: bold;
}
}
}
}
......@@ -202,6 +268,24 @@ div.course-wrapper {
}
}
}
div.ui-tabs {
border: 0;
@include border-radius(0);
margin: 0;
padding: 0;
.ui-tabs-nav {
background: none;
border: 0;
margin-bottom: lh(.5);
}
.ui-tabs-panel {
@include border-radius(0);
padding: 0;
}
}
}
&.closed {
......
nav.sequence-nav {
@extend .topbar;
@include box-sizing(border-box);
border-bottom: 1px solid darken($cream, 20%);
margin-bottom: $body-line-height;
position: relative;
@include border-top-right-radius(4px);
ol {
border-bottom: 1px solid darken($cream, 20%);
@include box-sizing(border-box);
display: table;
height: 100%;
padding-right: flex-grid(1, 9);
width: 100%;
......@@ -61,116 +62,117 @@ nav.sequence-nav {
display: block;
height: 17px;
padding: 15px 0 14px;
position: relative;
@include transition(all, .4s, $ease-in-out-quad);
width: 100%;
// @media screen and (max-width: 800px) {
// padding: 12px 8px;
// }
//video
&.seq_video_inactive {
@extend .inactive;
background-image: url('../images/sequence-nav/video-icon-normal.png');
background-position: center;
}
&.seq_video_visited {
@extend .visited;
background-image: url('../images/sequence-nav/video-icon-visited.png');
background-position: center;
}
//video
&.seq_video_inactive {
@extend .inactive;
background-image: url('../images/sequence-nav/video-icon-normal.png');
background-position: center;
}
&.seq_video_active {
@extend .active;
background-image: url('../images/sequence-nav/video-icon-current.png');
background-position: center;
}
&.seq_video_visited {
@extend .visited;
background-image: url('../images/sequence-nav/video-icon-visited.png');
background-position: center;
}
//other
&.seq_other_inactive {
@extend .inactive;
background-image: url('../images/sequence-nav/document-icon-normal.png');
background-position: center;
}
&.seq_video_active {
@extend .active;
background-image: url('../images/sequence-nav/video-icon-current.png');
background-position: center;
}
&.seq_other_visited {
@extend .visited;
background-image: url('../images/sequence-nav/document-icon-visited.png');
background-position: center;
}
//other
&.seq_other_inactive {
@extend .inactive;
background-image: url('../images/sequence-nav/document-icon-normal.png');
background-position: center;
}
&.seq_other_active {
@extend .active;
background-image: url('../images/sequence-nav/document-icon-current.png');
background-position: center;
}
&.seq_other_visited {
@extend .visited;
background-image: url('../images/sequence-nav/document-icon-visited.png');
background-position: center;
}
//vertical & problems
&.seq_vertical_inactive, &.seq_problem_inactive {
@extend .inactive;
background-image: url('../images/sequence-nav/list-icon-normal.png');
background-position: center;
}
&.seq_other_active {
@extend .active;
background-image: url('../images/sequence-nav/document-icon-current.png');
background-position: center;
}
&.seq_vertical_visited, &.seq_problem_visited {
@extend .visited;
background-image: url('../images/sequence-nav/list-icon-visited.png');
background-position: center;
}
//vertical & problems
&.seq_vertical_inactive, &.seq_problem_inactive {
@extend .inactive;
background-image: url('../images/sequence-nav/list-icon-normal.png');
background-position: center;
}
&.seq_vertical_active, &.seq_problem_active {
@extend .active;
background-image: url('../images/sequence-nav/list-icon-current.png');
background-position: center;
}
&.seq_vertical_visited, &.seq_problem_visited {
@extend .visited;
background-image: url('../images/sequence-nav/list-icon-visited.png');
background-position: center;
}
p {
// display: none;
// visibility: hidden;
background: #333;
color: #fff;
line-height: lh();
margin: 0px 0 0 -5px;
opacity: 0;
padding: 6px;
position: absolute;
text-shadow: 0 -1px 0 #000;
@include transition(all, .6s, $ease-in-out-quart);
white-space: pre-wrap;
z-index: 99;
&.shown {
margin-top: 4px;
opacity: 1;
&.seq_vertical_active, &.seq_problem_active {
@extend .active;
background-image: url('../images/sequence-nav/list-icon-current.png');
background-position: center;
}
&:empty {
background: none;
p {
background: #333;
color: #fff;
display: none;
line-height: lh();
left: 0px;
opacity: 0;
padding: 6px;
position: absolute;
top: 48px;
text-shadow: 0 -1px 0 #000;
@include transition(all, .1s, $ease-in-out-quart);
white-space: pre;
z-index: 99;
&:empty {
background: none;
&::after {
display: none;
}
}
&::after {
display: none;
background: #333;
content: " ";
display: block;
height: 10px;
left: 18px;
position: absolute;
top: -5px;
@include transform(rotate(45deg));
width: 10px;
}
}
&::after {
background: #333;
content: " ";
display: block;
height: 10px;
left: 18px;
position: absolute;
top: -5px;
@include transform(rotate(45deg));
width: 10px;
&:hover {
p {
display: block;
margin-top: 4px;
opacity: 1;
}
}
}
}
}
ul {
margin-right: 1px;
list-style: none !important;
height: 100%;
position: absolute;
right: 0;
top: 0;
......@@ -220,6 +222,7 @@ nav.sequence-nav {
&.next {
a {
background-image: url('../images/sequence-nav/next-icon.png');
@include border-top-right-radius(4px);
&:hover {
background-color: none;
......@@ -232,26 +235,20 @@ nav.sequence-nav {
section.course-content {
div#seq_content {
margin-bottom: 60px;
}
position: relative;
nav.sequence-bottom {
bottom: (-(lh()));
position: relative;
margin: lh(2) 0 0;
text-align: center;
ul {
@extend .clearfix;
background-color: darken(#F6EFD4, 5%);
background-color: darken($cream, 5%);
border: 1px solid darken(#f6efd4, 20%);
border-bottom: 0;
@include border-radius(3px 3px 0 0);
@include border-radius(3px);
@include box-shadow(inset 0 0 0 1px lighten(#f6efd4, 5%));
margin: 0 auto;
overflow: hidden;
width: 106px;
@include inline-block();
li {
float: left;
......@@ -264,15 +261,13 @@ section.course-content {
background-repeat: no-repeat;
border-bottom: none;
display: block;
display: block;
padding: lh(.75) 4px;
padding: lh(.5) 4px;
text-indent: -9999px;
@include transition(all, .4s, $ease-in-out-quad);
width: 45px;
&:hover {
background-color: darken($cream, 10%);
color: darken(#F6EFD4, 60%);
color: darken($cream, 60%);
opacity: .5;
text-decoration: none;
......@@ -288,6 +283,7 @@ section.course-content {
&.prev {
a {
background-image: url('../images/sequence-nav/previous-icon.png');
border-right: 1px solid darken(#f6efd4, 20%);
&:hover {
background-color: none;
......
......@@ -26,6 +26,7 @@ section.course-index {
}
&.ui-state-active {
@include background-image(linear-gradient(-90deg, rgb(245,245,245), rgb(225,225,225)));
@extend .active;
}
}
......@@ -33,58 +34,99 @@ section.course-index {
ul.ui-accordion-content {
@include border-radius(0);
@include box-shadow( inset -1px 0 0 #e6e6e6);
@include box-shadow(inset -1px 0 0 #e6e6e6);
background: #dadada;
border: none;
border-bottom: 1px solid #c3c3c3;
font-size: 12px;
margin: 0;
// overflow: visible;
padding: 1em 1.5em;
li {
background: transparent;
border: 1px solid transparent;
@include border-radius(4px);
margin-bottom: lh(.5);
position: relative;
&.active {
font-weight: bold;
p.subtitle {
font-weight: normal;
}
// &:after {
// content: " ";
// width: 16px;
// height: 16px;
// position: absolute;
// right: -35px;
// top: 7px;
// display: block;
// background-color: #dadada;
// border-top: 1px solid #c3c3c3;
// border-right: 1px solid #c3c3c3;
// z-index: 99;
// @include transform(rotate(45deg));
// }
}
padding: 5px 36px 5px 10px;
a {
text-decoration: none;
margin-bottom: lh(.5);
display: block;
color: #000;
&:hover {
color: #666;
}
color: #666;
p {
font-weight: bold;
margin-bottom: 0;
&.subtitle {
span.subtitle {
color: #666;
font-weight: normal;
display: block;
}
}
}
&:after {
background: transparent;
border-top: 1px solid rgb(180,180,180);
border-right: 1px solid rgb(180,180,180);
content: "";
display: block;
height: 12px;
margin-top: -6px;
opacity: 0;
position: absolute;
top: 50%;
right: 30px;
@include transform(rotate(45deg));
width: 12px;
}
&:hover {
@include background-image(linear-gradient(-90deg, rgba(245,245,245, 0.4), rgba(230,230,230, 0.4)));
border-color: rgb(200,200,200);
&:after {
opacity: 1;
right: 15px;
@include transition(all, 0.2s, linear);
}
> a p {
color: #333;
}
}
&:active {
@include box-shadow(inset 0 1px 14px 0 rgba(0,0,0, 0.1));
top: 1px;
&:after {
opacity: 1;
right: 15px;
}
}
&.active {
background: rgb(240,240,240);
@include background-image(linear-gradient(-90deg, rgb(245,245,245), rgb(230,230,230)));
border-color: rgb(200,200,200);
font-weight: bold;
> a p {
color: #333;
}
span.subtitle {
font-weight: normal;
}
&:after {
opacity: 1;
right: 15px;
}
}
}
}
}
......
......@@ -42,16 +42,6 @@ div.answer-block {
padding-top: 20px;
width: 100%;
div.official-stamp {
background: $mit-red;
color: #fff;
font-size: 12px;
margin-top: 10px;
padding: 2px 5px;
text-align: center;
margin-left: -1px;
}
img.answer-img-accept {
margin: 10px 0px 10px 16px;
}
......
div.question-header {
div.official-stamp {
background: $mit-red;
color: #fff;
font-size: 12px;
margin-top: 10px;
padding: 2px 5px;
text-align: center;
margin-left: -1px;
}
div.vote-buttons {
display: inline-block;
float: left;
......
li.calc-main {
bottom: -36px;
bottom: -126px;
left: 0;
position: fixed;
@include transition(bottom);
-webkit-appearance: none;
width: 100%;
z-index: 99;
&.open {
bottom: -36px;
div#calculator_wrapper form div.input-wrapper div.help-wrapper dl {
display: block;
}
}
a.calc {
@include hide-text;
background: url("../images/calc-icon.png") rgba(#111, .9) no-repeat center;
border-bottom: 0;
@include border-radius(3px 3px 0 0);
color: #fff;
float: right;
margin-right: 10px;
@include border-radius(3px 3px 0 0);
height: 20px;
@include hide-text;
@include inline-block;
margin-right: 10px;
padding: 8px 12px;
width: 16px;
height: 20px;
position: relative;
top: -36px;
width: 16px;
&:hover {
opacity: .8;
......@@ -30,14 +41,15 @@ li.calc-main {
div#calculator_wrapper {
background: rgba(#111, .9);
clear: both;
max-height: 90px;
position: relative;
top: -36px;
clear: both;
form {
padding: lh();
@extend .clearfix;
@include box-sizing(border-box);
padding: lh();
input#calculator_button {
background: #111;
......@@ -46,13 +58,14 @@ li.calc-main {
@include box-shadow(none);
@include box-sizing(border-box);
color: #fff;
float: left;
font-size: 30px;
font-weight: bold;
margin: 0 (flex-gutter() / 2);
padding: 0;
text-shadow: none;
-webkit-appearance: none;
width: flex-grid(.5) + flex-gutter();
float: left;
margin: 0 (flex-gutter() / 2);
&:hover {
color: #333;
......@@ -70,29 +83,31 @@ li.calc-main {
font-weight: bold;
margin: 1px 0 0;
padding: 10px;
-webkit-appearance: none;
width: flex-grid(4);
}
div.input-wrapper {
position: relative;
@extend .clearfix;
width: flex-grid(7.5);
margin: 0;
float: left;
margin: 0;
position: relative;
width: flex-grid(7.5);
input#calculator_input {
border: none;
@include box-shadow(none);
@include box-sizing(border-box);
font-size: 16px;
padding: 10px;
width: 100%;
&:focus {
outline: none;
input#calculator_input {
border: none;
@include box-shadow(none);
@include box-sizing(border-box);
font-size: 16px;
padding: 10px;
-webkit-appearance: none;
width: 100%;
&:focus {
outline: none;
border: none;
}
}
}
div.help-wrapper {
position: absolute;
......@@ -100,10 +115,10 @@ li.calc-main {
top: 15px;
a {
background: url("../images/info-icon.png") center center no-repeat;
height: 17px;
@include hide-text;
width: 17px;
height: 17px;
background: url("../images/info-icon.png") center center no-repeat;
}
dl {
......@@ -111,13 +126,14 @@ li.calc-main {
@include border-radius(3px);
@include box-shadow(0 0 3px #999);
color: #333;
display: none;
opacity: 0;
padding: 10px;
position: absolute;
right: -40px;
top: -110px;
width: 500px;
@include transition();
width: 500px;
&.shown {
opacity: 1;
......
......@@ -68,11 +68,11 @@ footer {
}
a {
border-bottom: 0;
display: block;
height: 29px;
width: 28px;
text-indent: -9999px;
border-bottom: 0;
width: 28px;
&:hover {
opacity: .8;
......
......@@ -100,12 +100,12 @@ div.header-wrapper {
float: left;
a {
border: none;
color: #fff;
display: block;
font-style: normal;
font-weight: bold;
padding: 10px lh() 8px;
border: none;
font-style: normal;
@media screen and (max-width: 1020px) {
padding: 10px lh(.7) 8px;
......@@ -125,10 +125,10 @@ div.header-wrapper {
ul {
li {
padding: auto;
display: table-cell;
width: 16.6666666667%;
padding: auto;
text-align: center;
width: 16.6666666667%;
}
}
}
......
......@@ -2,11 +2,11 @@ html {
margin-top: 0;
body {
background: #f4f4f4; //#f3f1e5
color: $dark-gray;
font: $body-font-size $body-font-family;
text-align: center;
margin: 0;
background: #f4f4f4; //#f3f1e5
text-align: center;
section.main-content {
@extend .clearfix;
......@@ -17,7 +17,7 @@ html {
@include box-shadow(0 0 4px #dfdfdf);
@include box-sizing(border-box);
margin-top: 3px;
// overflow: hidden;
overflow: hidden;
@media print {
border-bottom: 0;
......@@ -30,6 +30,18 @@ html {
}
}
div.qtip {
div.ui-tooltip-content {
background: #000;
background: rgba(#000, .8);
border: none;
color: #fff;
font: 12px $body-font-family;
margin-right: -20px;
margin-top: -30px;
}
}
section.outside-app {
@extend .main-content;
max-width: 600px;
......
#lean_overlay {
background: #000;
display: none;
height:100%;
left: 0px;
position: fixed;
z-index:100;
top: 0px;
left: 0px;
height:100%;
width:100%;
background: #000;
display: none;
z-index:100;
}
div.leanModal_box {
......@@ -31,8 +31,8 @@ div.leanModal_box {
z-index: 2;
&:hover{
text-decoration: none;
color: $mit-red;
text-decoration: none;
}
}
......@@ -55,8 +55,8 @@ div.leanModal_box {
li {
&.terms, &.honor-code {
width: auto;
float: none;
width: auto;
}
div.tip {
......@@ -118,8 +118,8 @@ div.leanModal_box {
}
&.honor-code {
width: auto;
float: none;
width: auto;
}
label {
......@@ -128,8 +128,8 @@ div.leanModal_box {
}
#{$all-text-inputs}, textarea {
width: 100%;
@include box-sizing(border-box);
width: 100%;
}
input[type="checkbox"] {
......@@ -176,15 +176,15 @@ div#login {
ol {
li {
width: auto;
float: none;
width: auto;
}
}
}
div.lost-password {
text-align: left;
margin-top: lh();
text-align: left;
a {
color: #999;
......@@ -218,9 +218,9 @@ div#deactivate-account {
margin-bottom: lh(.5);
textarea, #{$all-text-inputs} {
@include box-sizing(border-box);
display: block;
width: 100%;
@include box-sizing(border-box);
}
textarea {
......
......@@ -2,5 +2,5 @@
@import "base/reset", "base/font-face", "base/functions";
// pages
@import "index/variables", "index/extends", "index/base", "index/header", "index/footer", "index/index";
@import "marketing/variables", "marketing/extends", "marketing/base", "marketing/header", "marketing/footer", "marketing/index";
@import "layout/leanmodal";
......@@ -6,7 +6,7 @@ footer {
div.footer-wrapper {
border-top: 1px solid #e5e5e5;
padding: lh() 0;
background: url('../images/marketing/mit-logo.png') right center no-repeat;
background: url('/static/images/marketing/mit-logo.png') right center no-repeat;
@media screen and (max-width: 780px) {
background-position: left bottom;
......@@ -84,15 +84,15 @@ footer {
}
&.twitter a {
background: url('../images/marketing/twitter.png') 0 0 no-repeat;
background: url('/static/images/marketing/twitter.png') 0 0 no-repeat;
}
&.facebook a {
background: url('../images/marketing/facebook.png') 0 0 no-repeat;
background: url('/static/images/marketing/facebook.png') 0 0 no-repeat;
}
&.linkedin a {
background: url('../images/marketing/linkedin.png') 0 0 no-repeat;
background: url('/static/images/marketing/linkedin.png') 0 0 no-repeat;
}
}
}
......
......@@ -6,10 +6,10 @@ header.announcement {
-webkit-font-smoothing: antialiased;
&.home {
background: #e3e3e3 url("../images/marketing/shot-5-medium.jpg");
background: #e3e3e3 url("/static/images/marketing/shot-5-medium.jpg");
@media screen and (min-width: 1200px) {
background: #e3e3e3 url("../images/marketing/shot-5-large.jpg");
background: #e3e3e3 url("/static/images/marketing/shot-5-large.jpg");
}
div {
......@@ -33,14 +33,14 @@ header.announcement {
}
&.course {
background: #e3e3e3 url("../images/marketing/course-bg-small.jpg");
background: #e3e3e3 url("/static/images/marketing/course-bg-small.jpg");
@media screen and (min-width: 1200px) {
background: #e3e3e3 url("../images/marketing/course-bg-large.jpg");
background: #e3e3e3 url("/static/images/marketing/course-bg-large.jpg");
}
@media screen and (max-width: 1199px) and (min-width: 700px) {
background: #e3e3e3 url("../images/marketing/course-bg-medium.jpg");
background: #e3e3e3 url("/static/images/marketing/course-bg-medium.jpg");
}
div {
......
......@@ -20,6 +20,7 @@ section.index-content {
p {
line-height: lh();
margin-bottom: lh();
}
ul {
......@@ -221,22 +222,35 @@ section.index-content {
&.course {
h2 {
padding-top: lh(5);
background: url('../images/marketing/circuits-bg.jpg') 0 0 no-repeat;
background: url('/static/images/marketing/circuits-bg.jpg') 0 0 no-repeat;
@include background-size(contain);
@media screen and (max-width: 998px) and (min-width: 781px){
background: url('../images/marketing/circuits-medium-bg.jpg') 0 0 no-repeat;
background: url('/static/images/marketing/circuits-medium-bg.jpg') 0 0 no-repeat;
}
@media screen and (max-width: 780px) {
padding-top: lh(5);
background: url('../images/marketing/circuits-bg.jpg') 0 0 no-repeat;
background: url('/static/images/marketing/circuits-bg.jpg') 0 0 no-repeat;
}
@media screen and (min-width: 500px) and (max-width: 781px) {
padding-top: lh(8);
}
}
div.announcement {
p.announcement-button {
a {
margin-top: 0;
}
}
img {
max-width: 100%;
margin-bottom: lh();
}
}
}
......
form#wiki_revision {
float: left;
width: flex-grid(6, 9);
margin-right: flex-gutter(9);
width: flex-grid(6, 9);
label {
display: block;
margin-bottom: 7px ;
}
.CodeMirror-scroll {
min-height: 550px;
width: 100%;
}
.CodeMirror {
@extend textarea;
@include box-sizing(border-box);
font-family: monospace;
margin-bottom: 20px;
}
textarea {
@include box-sizing(border-box);
margin-bottom: 20px;
......@@ -32,25 +33,25 @@ form#wiki_revision {
}
#submit_delete {
@include box-shadow(none);
background: none;
border: none;
@include box-shadow(none);
color: #999;
float: right;
text-decoration: underline;
font-weight: normal;
text-decoration: underline;
}
input[type="submit"] {
margin-top: 20px;
}
}
#wiki_edit_instructions {
color: #666;
float: left;
width: flex-grid(3, 9);
margin-top: lh();
color: #666;
width: flex-grid(3, 9);
&:hover {
color: #333;
......@@ -58,16 +59,14 @@ form#wiki_revision {
.markdown-example {
background-color: #e3e3e3;
text-shadow: 0 1px 0 #fff;
line-height: 1.0;
margin: 5px 0 7px;
padding: {
top: 5px;
right: 2px;
bottom: 5px;
left: 5px;
}
margin: 5px 0 7px;
line-height: 1.0;
text-shadow: 0 1px 0 #fff;
}
}
......@@ -3,20 +3,20 @@ div#wiki_panel {
overflow: auto;
h2 {
padding: lh(.5) lh();
@extend .bottom-border;
font-size: 18px;
margin: 0 ;
@extend .bottom-border;
padding: lh(.5) lh();
}
input[type="button"] {
@extend h3;
@include transition();
color: lighten($text-color, 10%);
font-size: $body-font-size;
margin: 0 !important;
padding: 7px lh();
text-align: left;
@include transition();
width: 100%;
&:hover {
......@@ -28,8 +28,8 @@ div#wiki_panel {
ul {
li {
&.search {
@include box-shadow(0 1px 0 #eee);
border-bottom: 1px solid #d3d3d3;
@include box-shadow(0 1px 0 #eee);
padding: 7px lh();
label {
......@@ -49,15 +49,15 @@ div#wiki_panel {
div#wiki_create_form {
@extend .clearfix;
padding: 15px;
background: #d6d6d6;
border-bottom: 1px solid #bbb;
padding: 15px;
input[type="text"] {
margin-bottom: 6px;
@include box-sizing(border-box);
display: block;
margin-bottom: 6px;
width: 100%;
@include box-sizing(border-box);
}
ul {
......
......@@ -7,15 +7,14 @@ div.wiki-wrapper {
@extend .content;
position: relative;
header {
@extend .topbar;
height:46px;
@include box-shadow(inset 0 1px 0 white);
height:46px;
&:empty {
display: none !important;
border-bottom: 0;
display: none !important;
}
a {
......@@ -23,10 +22,10 @@ div.wiki-wrapper {
}
p {
float: left;
margin-bottom: 0;
color: darken($cream, 55%);
float: left;
line-height: 46px;
margin-bottom: 0;
padding-left: lh();
}
......@@ -48,8 +47,8 @@ div.wiki-wrapper {
@include box-shadow(inset 1px 0 0 lighten(#f6efd4, 5%));
color: darken($cream, 80%);
display: block;
font-weight: normal;
font-size: 12px;
font-weight: normal;
letter-spacing: 1px;
line-height: 46px;
margin: 0;
......@@ -89,15 +88,15 @@ div.wiki-wrapper {
width: flex-grid(2.5, 9);
@media screen and (max-width:900px) {
border-right: 0;
display: block;
width: auto;
border-right: 0;
}
@media print {
border-right: 0;
display: block;
width: auto;
border-right: 0;
}
}
......@@ -106,9 +105,9 @@ div.wiki-wrapper {
}
section.results {
border-left: 1px dashed #ddd;
@include box-sizing(border-box);
display: inline-block;
border-left: 1px dashed #ddd;
float: left;
padding-left: 10px;
width: flex-grid(6.5, 9);
......@@ -123,8 +122,8 @@ div.wiki-wrapper {
@media print {
display: block;
width: auto;
padding: 0;
width: auto;
canvas, img {
page-break-inside: avoid;
......@@ -140,14 +139,15 @@ div.wiki-wrapper {
}
li {
border-bottom: 1px solid #eee;
list-style: none;
margin: 0;
padding: 10px 0;
border-bottom: 1px solid #eee;
&:last-child {
border-bottom: 0;
}
h3 {
font-size: 18px;
font-weight: normal;
......@@ -155,6 +155,5 @@ div.wiki-wrapper {
}
}
}
}
}
......@@ -5,6 +5,30 @@ import tempfile
import djcelery
### Dark code. Should be enabled in local settings for devel.
ENABLE_MULTICOURSE = False # set to False to disable multicourse display (see lib.util.views.mitxhome)
QUICKEDIT = False
###
MITX_ROOT_URL = ''
COURSE_NAME = "6.002_Spring_2012"
COURSE_NUMBER = "6.002x"
COURSE_TITLE = "Circuits and Electronics"
COURSE_DEFAULT = '6.002_Spring_2012'
COURSE_SETTINGS = {'6.002_Spring_2012': {'number' : '6.002x',
'title' : 'Circuits and Electronics',
'xmlpath': '6002x/',
}
}
ROOT_URLCONF = 'urls'
# from settings2.askbotsettings import LIVESETTINGS_OPTIONS
DEFAULT_GROUPS = []
......@@ -28,7 +52,6 @@ sys.path.append(BASE_DIR + "/mitx/lib")
COURSEWARE_ENABLED = True
ASKBOT_ENABLED = True
CSRF_COOKIE_DOMAIN = '127.0.0.1'
# Defaults to be overridden
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
......@@ -39,8 +62,8 @@ DEFAULT_FEEDBACK_EMAIL = 'feedback@mitx.mit.edu'
GENERATE_RANDOM_USER_CREDENTIALS = False
WIKI_REQUIRE_LOGIN_EDIT = True
WIKI_REQUIRE_LOGIN_VIEW = True
SIMPLE_WIKI_REQUIRE_LOGIN_EDIT = True
SIMPLE_WIKI_REQUIRE_LOGIN_VIEW = False
PERFSTATS = False
......@@ -116,9 +139,11 @@ MIDDLEWARE_CLASSES = (
'django.middleware.csrf.CsrfViewMiddleware',
#'django.contrib.auth.middleware.AuthenticationMiddleware',
'cache_toolbox.middleware.CacheBackedAuthenticationMiddleware',
'masquerade.middleware.MasqueradeMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'track.middleware.TrackMiddleware',
'mitxmako.middleware.MakoMiddleware',
#'ssl_auth.ssl_auth.NginxProxyHeaderMiddleware', # ssl authentication behind nginx proxy
#'debug_toolbar.middleware.DebugToolbarMiddleware',
# Uncommenting the following will prevent csrf token from being re-set if you
......@@ -146,6 +171,10 @@ INSTALLED_APPS = (
'circuit',
'perfstats',
'util',
'masquerade',
'django_jasmine',
#'ssl_auth', ## Broken. Disabled for now.
'multicourse', # multiple courses
# Uncomment the next line to enable the admin:
# 'django.contrib.admin',
# Uncomment the next line to enable admin documentation:
......@@ -346,6 +375,7 @@ PROJECT_ROOT = os.path.dirname(__file__)
TEMPLATE_CONTEXT_PROCESSORS = (
'django.core.context_processors.request',
'django.core.context_processors.static',
'askbot.context.application_settings',
#'django.core.context_processors.i18n',
'askbot.user_messages.context_processors.user_messages',#must be before auth
......@@ -369,8 +399,8 @@ INSTALLED_APPS = INSTALLED_APPS + (
CACHE_MIDDLEWARE_ANONYMOUS_ONLY = True
ASKBOT_URL = 'discussion/'
LOGIN_REDIRECT_URL = '/'
LOGIN_URL = '/'
LOGIN_REDIRECT_URL = MITX_ROOT_URL + '/'
LOGIN_URL = MITX_ROOT_URL + '/'
# ASKBOT_UPLOADED_FILES_URL = '%s%s' % (ASKBOT_URL, 'upfiles/')
ALLOW_UNICODE_SLUGS = False
......@@ -500,10 +530,11 @@ LIVESETTINGS_OPTIONS = {
'CUSTOM_HEADER' : u'',
'CUSTOM_HTML_HEAD' : u'',
'CUSTOM_JS' : u'',
'SITE_FAVICON' : u'/images/favicon.gif',
'SITE_LOGO_URL' : u'/images/logo.gif',
'MITX_ROOT_URL' : MITX_ROOT_URL, # for askbot header.html file
'SITE_FAVICON' : unicode(MITX_ROOT_URL) + u'/images/favicon.gif',
'SITE_LOGO_URL' :unicode(MITX_ROOT_URL) + u'/images/logo.gif',
'SHOW_LOGO' : False,
'LOCAL_LOGIN_ICON' : u'/images/pw-login.gif',
'LOCAL_LOGIN_ICON' : unicode(MITX_ROOT_URL) + u'/images/pw-login.gif',
'ALWAYS_SHOW_ALL_UI_FUNCTIONS' : False,
'ASKBOT_DEFAULT_SKIN' : u'default',
'USE_CUSTOM_HTML_HEAD' : False,
......@@ -536,12 +567,12 @@ LIVESETTINGS_OPTIONS = {
'SIGNIN_WORDPRESS_ENABLED' : True,
'SIGNIN_WORDPRESS_SITE_ENABLED' : False,
'SIGNIN_YAHOO_ENABLED' : True,
'WORDPRESS_SITE_ICON' : u'/images/logo.gif',
'WORDPRESS_SITE_ICON' : unicode(MITX_ROOT_URL) + u'/images/logo.gif',
'WORDPRESS_SITE_URL' : '',
},
'LICENSE_SETTINGS' : {
'LICENSE_ACRONYM' : u'cc-by-sa',
'LICENSE_LOGO_URL' : u'/images/cc-by-sa.png',
'LICENSE_LOGO_URL' : unicode(MITX_ROOT_URL) + u'/images/cc-by-sa.png',
'LICENSE_TITLE' : u'Creative Commons Attribution Share Alike 3.0',
'LICENSE_URL' : 'http://creativecommons.org/licenses/by-sa/3.0/legalcode',
'LICENSE_USE_LOGO' : True,
......@@ -682,3 +713,5 @@ if MAKO_MODULE_DIR == None:
djcelery.setup_loader()
# Jasmine Settings
JASMINE_TEST_DIRECTORY = PROJECT_DIR+'/templates/coffee'
This source diff could not be displayed because it is too large. You can view the blob instead.
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