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 @@ ...@@ -4,6 +4,9 @@
*.swp *.swp
*.orig *.orig
*.DS_Store *.DS_Store
:2e_*
:2e#
.AppleDouble
database.sqlite database.sqlite
courseware/static/js/mathjax/* courseware/static/js/mathjax/*
db.newaskbot db.newaskbot
......
...@@ -78,17 +78,23 @@ def evaluator(variables, functions, string, cs=False): ...@@ -78,17 +78,23 @@ def evaluator(variables, functions, string, cs=False):
# log.debug("functions: {0}".format(functions)) # log.debug("functions: {0}".format(functions))
# log.debug("string: {0}".format(string)) # 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 = copy.copy(default_variables)
all_variables.update(variables)
all_functions = copy.copy(default_functions) 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) all_functions.update(functions)
if not cs: if not cs:
string_cs = string.lower() string_cs = string.lower()
for v in all_variables.keys(): all_functions = lower_dict(all_functions)
all_variables[v.lower()]=all_variables[v] all_variables = lower_dict(all_variables)
for f in all_functions.keys():
all_functions[f.lower()]=all_functions[f]
CasedLiteral = CaselessLiteral CasedLiteral = CaselessLiteral
else: else:
string_cs = string 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 hashlib
import json
import logging import logging
import os import os
import re import re
...@@ -14,9 +22,11 @@ try: # This lets us do __name__ == ='__main__' ...@@ -14,9 +22,11 @@ try: # This lets us do __name__ == ='__main__'
from student.models import UserProfile from student.models import UserProfile
from student.models import UserTestGroup 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 util.cache import cache
from multicourse import multicourse_settings
except: except:
print "Could not import/content_parser"
settings = None settings = None
''' This file will eventually form an abstraction layer between the ''' This file will eventually form an abstraction layer between the
...@@ -97,20 +107,9 @@ def item(l, default="", process=lambda x:x): ...@@ -97,20 +107,9 @@ def item(l, default="", process=lambda x:x):
def id_tag(course): def id_tag(course):
''' Tag all course elements with unique IDs ''' ''' 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 import courseware.modules
default_ids = courseware.modules.get_default_ids() default_ids = courseware.modules.get_default_ids()
#print default_ids, old_ids
#print default_ids == old_ids
# Tag elements with unique IDs # Tag elements with unique IDs
elements = course.xpath("|".join(['//'+c for c in default_ids])) elements = course.xpath("|".join(['//'+c for c in default_ids]))
for elem in elements: for elem in elements:
...@@ -153,6 +152,9 @@ def propogate_downward_tag(element, attribute_name, parent_attribute = None): ...@@ -153,6 +152,9 @@ def propogate_downward_tag(element, attribute_name, parent_attribute = None):
return return
def user_groups(user): def user_groups(user):
if not user.is_authenticated():
return []
# TODO: Rewrite in Django # TODO: Rewrite in Django
key = 'user_group_names_{user.id}'.format(user=user) key = 'user_group_names_{user.id}'.format(user=user)
cache_expiration = 60 * 60 # one hour cache_expiration = 60 * 60 # one hour
...@@ -177,15 +179,23 @@ def course_xml_process(tree): ...@@ -177,15 +179,23 @@ def course_xml_process(tree):
propogate_downward_tag(tree, "due") propogate_downward_tag(tree, "due")
propogate_downward_tag(tree, "graded") propogate_downward_tag(tree, "graded")
propogate_downward_tag(tree, "graceperiod") propogate_downward_tag(tree, "graceperiod")
propogate_downward_tag(tree, "showanswer")
propogate_downward_tag(tree, "rerandomize")
return tree return tree
def course_file(user): def course_file(user,coursename=None):
''' Given a user, return course.xml''' ''' 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) groups = user_groups(user)
options = {'dev_content':settings.DEV_CONTENT, options = {'dev_content':settings.DEV_CONTENT,
'groups' : groups} 'groups' : groups}
...@@ -207,13 +217,24 @@ def course_file(user): ...@@ -207,13 +217,24 @@ def course_file(user):
return tree return tree
def section_file(user, section): def section_file(user, section, coursename=None, dironly=False):
''' Given a user and the name of a section, return that section '''
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" filename = section+".xml"
if filename not in 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
print filename+" not in "+str(os.listdir(settings.DATA_DIR + '/sections/')) 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 return None
options = {'dev_content':settings.DEV_CONTENT, options = {'dev_content':settings.DEV_CONTENT,
...@@ -223,7 +244,7 @@ def section_file(user, section): ...@@ -223,7 +244,7 @@ def section_file(user, section):
return tree 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 ''' Get XML for a module based on module and module_id. Assumes
module occurs once in courseware XML file or hidden section. ''' module occurs once in courseware XML file or hidden section. '''
# Sanitize input # Sanitize input
...@@ -236,14 +257,15 @@ def module_xml(user, module, id_tag, module_id): ...@@ -236,14 +257,15 @@ def module_xml(user, module, id_tag, module_id):
id_tag=id_tag, id_tag=id_tag,
id=module_id) id=module_id)
#result_set=doc.xpathEval(xpath_search) #result_set=doc.xpathEval(xpath_search)
doc = course_file(user) doc = course_file(user,coursename)
section_list = (s[:-4] for s in os.listdir(settings.DATA_DIR+'/sections') if s[-4:]=='.xml') 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) result_set=doc.xpath(xpath_search)
if len(result_set)<1: if len(result_set)<1:
for section in section_list: for section in section_list:
try: try:
s = section_file(user, section) s = section_file(user, section, coursename)
except etree.XMLSyntaxError: except etree.XMLSyntaxError:
ex= sys.exc_info() ex= sys.exc_info()
raise ContentException("Malformed XML in " + section+ "("+str(ex[1].msg)+")") 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 ...@@ -6,9 +6,9 @@ from django.core.management.base import BaseCommand
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import User from django.contrib.auth.models import User
from mitx.courseware.content_parser import course_file from courseware.content_parser import course_file
import mitx.courseware.module_render import courseware.module_render
import mitx.courseware.modules import courseware.modules
class Command(BaseCommand): class Command(BaseCommand):
help = "Does basic validity tests on course.xml." help = "Does basic validity tests on course.xml."
...@@ -25,15 +25,15 @@ class Command(BaseCommand): ...@@ -25,15 +25,15 @@ class Command(BaseCommand):
check = False check = False
print "Confirming all modules render. Nothing should print during this step. " print "Confirming all modules render. Nothing should print during this step. "
for module in course.xpath('//problem|//html|//video|//vertical|//sequential|/tab'): 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 # TODO: Abstract this out in render_module.py
try: try:
instance=module_class(etree.tostring(module), module_class(etree.tostring(module),
module.get('id'), module.get('id'),
ajax_url='', ajax_url='',
state=None, state=None,
track_function = lambda x,y,z:None, track_function = lambda x,y,z:None,
render_function = lambda x: {'content':'','destroy_js':'','init_js':'','type':'video'}) render_function = lambda x: {'content':'','destroy_js':'','init_js':'','type':'video'})
except: except:
print "==============> Error in ", etree.tostring(module) print "==============> Error in ", etree.tostring(module)
check = False check = False
......
import StringIO
import json
import logging import logging
import os
import sys
import sys
import urllib
import uuid
from lxml import etree 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 Http404
from django.http import HttpResponse from django.http import HttpResponse
from django.shortcuts import redirect from django.shortcuts import redirect
from django.template import Context from django.template import Context
from django.template import Context, loader 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 models import StudentModule
from student.models import UserProfile
import track.views import track.views
import courseware.content_parser as content_parser
import courseware.modules import courseware.modules
log = logging.getLogger("mitx.courseware") 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): def object_cache(cache, user, module_type, module_id):
# We don't look up on user -- all queries include user # We don't look up on user -- all queries include user
# Additional lookup would require a DB hit the way Django # Additional lookup would require a DB hit the way Django
...@@ -51,60 +60,19 @@ def make_track_function(request): ...@@ -51,60 +60,19 @@ def make_track_function(request):
return track.views.server_track(request, event_type, event, page='x_module') return track.views.server_track(request, event_type, event, page='x_module')
return f 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): def grade_histogram(module_id):
''' Print out a histogram of grades on a given problem. ''' Print out a histogram of grades on a given problem.
Part of staff member debug info. Part of staff member debug info.
''' '''
from django.db import connection, transaction from django.db import connection
cursor = connection.cursor() 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]) 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()) grades = list(cursor.fetchall())
print grades
grades.sort(key=lambda x:x[0]) # Probably not necessary grades.sort(key=lambda x:x[0]) # Probably not necessary
if (len(grades) == 1 and grades[0][0] == None):
return []
return grades return grades
def render_x_module(user, request, xml_module, module_object_preload): 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): ...@@ -125,31 +93,51 @@ def render_x_module(user, request, xml_module, module_object_preload):
else: else:
state = smod.state state = smod.state
# get coursename if stored
if 'coursename' in request.session: coursename = request.session['coursename']
else: coursename = None
# Create a new instance # Create a new instance
ajax_url = '/modx/'+module_type+'/'+module_id+'/' ajax_url = settings.MITX_ROOT_URL + '/modx/'+module_type+'/'+module_id+'/'
instance=module_class(etree.tostring(xml_module), 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, module_id,
ajax_url=ajax_url, state=state)
state=state,
track_function = make_track_function(request),
render_function = lambda x: render_module(user, request, x, module_object_preload))
# If instance wasn't already in the database, create it # If instance wasn't already in the database, and this
if not smod: # isn't a guest user, create it
if not smod and user.is_authenticated():
smod=StudentModule(student=user, smod=StudentModule(student=user,
module_type = module_type, module_type = module_type,
module_id=module_id, module_id=module_id,
state=instance.get_state()) state=instance.get_state())
smod.save() smod.save()
module_object_preload.append(smod) module_object_preload.append(smod)
# Grab content # Grab content
content = instance.get_html() 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: 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), 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, content = {'content':content,
"destroy_js":instance.get_destroy_js(), "destroy_js":destory_js,
'init_js':instance.get_init_js(), 'init_js':init_js,
'type':module_type} 'type':module_type}
return content return content
......
...@@ -16,13 +16,12 @@ import traceback ...@@ -16,13 +16,12 @@ import traceback
from lxml import etree from lxml import etree
## TODO: Abstract out from Django ## TODO: Abstract out from Django
from django.conf import settings from mitxmako.shortcuts import render_to_string
from mitxmako.shortcuts import render_to_response, render_to_string
from django.http import Http404
from x_module import XModule from x_module import XModule
from courseware.capa.capa_problem import LoncapaProblem, StudentInputError from courseware.capa.capa_problem import LoncapaProblem, StudentInputError
import courseware.content_parser as content_parser import courseware.content_parser as content_parser
from multicourse import multicourse_settings
log = logging.getLogger("mitx.courseware") log = logging.getLogger("mitx.courseware")
...@@ -92,10 +91,13 @@ class Module(XModule): ...@@ -92,10 +91,13 @@ class Module(XModule):
# User submitted a problem, and hasn't reset. We don't want # User submitted a problem, and hasn't reset. We don't want
# more submissions. # more submissions.
if self.lcp.done and self.rerandomize == "always": if self.lcp.done and self.rerandomize == "always":
#print "!"
check_button = False check_button = False
save_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 # User hasn't submitted an answer yet -- we don't want resets
if not self.lcp.done: if not self.lcp.done:
reset_button = False reset_button = False
...@@ -114,25 +116,26 @@ class Module(XModule): ...@@ -114,25 +116,26 @@ class Module(XModule):
if len(explain) == 0: if len(explain) == 0:
explain = False explain = False
html=render_to_string('problem.html', context = {'problem' : content,
{'problem' : content, 'id' : self.item_id,
'id' : self.item_id, 'check_button' : check_button,
'check_button' : check_button, 'reset_button' : reset_button,
'reset_button' : reset_button, 'save_button' : save_button,
'save_button' : save_button, 'answer_available' : self.answer_available(),
'answer_available' : self.answer_available(), 'ajax_url' : self.ajax_url,
'ajax_url' : self.ajax_url, 'attempts_used': self.attempts,
'attempts_used': self.attempts, 'attempts_allowed': self.max_attempts,
'attempts_allowed': self.max_attempts, 'explain': explain,
'explain': explain }
})
html=render_to_string('problem.html', context)
if encapsulate: if encapsulate:
html = '<div id="main_{id}">'.format(id=self.item_id)+html+"</div>" html = '<div id="main_{id}">'.format(id=self.item_id)+html+"</div>"
return html return html
def __init__(self, xml, item_id, ajax_url=None, track_url=None, state=None, track_function=None, render_function = None, meta = None): def __init__(self, system, xml, item_id, state=None):
XModule.__init__(self, xml, item_id, ajax_url, track_url, state, track_function, render_function) XModule.__init__(self, system, xml, item_id, state)
self.attempts = 0 self.attempts = 0
self.max_attempts = None self.max_attempts = None
...@@ -185,17 +188,24 @@ class Module(XModule): ...@@ -185,17 +188,24 @@ class Module(XModule):
if state!=None and 'attempts' in state: if state!=None and 'attempts' in state:
self.attempts=state['attempts'] self.attempts=state['attempts']
self.filename=content_parser.item(dom2.xpath('/problem/@filename')) self.filename="problems/"+content_parser.item(dom2.xpath('/problem/@filename'))+".xml"
filename=settings.DATA_DIR+"/problems/"+self.filename+".xml"
self.name=content_parser.item(dom2.xpath('/problem/@name')) self.name=content_parser.item(dom2.xpath('/problem/@name'))
self.weight=content_parser.item(dom2.xpath('/problem/@weight')) self.weight=content_parser.item(dom2.xpath('/problem/@weight'))
if self.rerandomize == 'never': if self.rerandomize == 'never':
seed = 1 seed = 1
else: else:
seed = None 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): 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': if dispatch=='problem_get':
response = self.get_problem(get) response = self.get_problem(get)
elif False: #self.close_date > elif False: #self.close_date >
...@@ -241,17 +251,23 @@ class Module(XModule): ...@@ -241,17 +251,23 @@ class Module(XModule):
return True return True
if self.show_answer == 'closed' and not self.closed(): if self.show_answer == 'closed' and not self.closed():
return False return False
print "aa", self.show_answer if self.show_answer == 'always':
raise Http404 return True
raise self.system.exception404 #TODO: Not 404
def get_answer(self, get): 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(): if not self.answer_available():
raise Http404 raise self.system.exception404
else: else:
return json.dumps(self.lcp.get_question_answers(), answers = self.lcp.get_question_answers()
return json.dumps(answers,
cls=ComplexEncoder) cls=ComplexEncoder)
# Figure out if we should move these to capa_problem? # Figure out if we should move these to capa_problem?
def get_problem(self, get): def get_problem(self, get):
''' Same as get_problem_html -- if we want to reconfirm we ''' Same as get_problem_html -- if we want to reconfirm we
...@@ -265,66 +281,56 @@ class Module(XModule): ...@@ -265,66 +281,56 @@ class Module(XModule):
event_info['state'] = self.lcp.get_state() event_info['state'] = self.lcp.get_state()
event_info['filename'] = self.filename event_info['filename'] = self.filename
# make a dict of all the student responses ("answers").
answers=dict() answers=dict()
# input_resistor_1 ==> resistor_1 # input_resistor_1 ==> resistor_1
for key in get: for key in get:
answers['_'.join(key.split('_')[1:])]=get[key] answers['_'.join(key.split('_')[1:])]=get[key]
# print "XXX", answers, get
event_info['answers']=answers event_info['answers']=answers
# Too late. Cannot submit # Too late. Cannot submit
if self.closed(): if self.closed():
event_info['failure']='closed' event_info['failure']='closed'
self.tracker('save_problem_check_fail', event_info) self.tracker('save_problem_check_fail', event_info)
print "cp" raise self.system.exception404
raise Http404
# Problem submitted. Student should reset before checking # Problem submitted. Student should reset before checking
# again. # again.
if self.lcp.done and self.rerandomize == "always": if self.lcp.done and self.rerandomize == "always":
event_info['failure']='unreset' event_info['failure']='unreset'
self.tracker('save_problem_check_fail', event_info) self.tracker('save_problem_check_fail', event_info)
print "cpdr" raise self.system.exception404
raise Http404
try: try:
old_state = self.lcp.get_state() old_state = self.lcp.get_state()
lcp_id = self.lcp.problem_id lcp_id = self.lcp.problem_id
filename = self.lcp.filename
correct_map = self.lcp.grade_answers(answers) correct_map = self.lcp.grade_answers(answers)
except StudentInputError as inst: 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() traceback.print_exc()
# print {'error':sys.exc_info(),
# 'answers':answers,
# 'seed':self.lcp.seed,
# 'filename':self.lcp.filename}
return json.dumps({'success':inst.message}) return json.dumps({'success':inst.message})
except: 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() traceback.print_exc()
raise Exception,"error in capa_module"
return json.dumps({'success':'Unknown Error'}) return json.dumps({'success':'Unknown Error'})
self.attempts = self.attempts + 1 self.attempts = self.attempts + 1
self.lcp.done=True self.lcp.done=True
success = 'correct' success = 'correct'
for i in correct_map: for i in correct_map:
if correct_map[i]!='correct': if correct_map[i]!='correct':
success = 'incorrect' success = 'incorrect'
js=json.dumps({'correct_map' : correct_map,
'success' : success})
event_info['correct_map']=correct_map event_info['correct_map']=correct_map
event_info['success']=success event_info['success']=success
self.tracker('save_problem_check', event_info) 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): def save_problem(self, get):
event_info = dict() event_info = dict()
...@@ -382,8 +388,7 @@ class Module(XModule): ...@@ -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.questions=dict() # Detailed info about questions in problem instance. TODO: Should be by id and not lid.
self.lcp.seed=None self.lcp.seed=None
filename=settings.DATA_DIR+"problems/"+self.filename+".xml" self.lcp=LoncapaProblem(self.filestore.open(self.filename), self.item_id, self.lcp.get_state())
self.lcp=LoncapaProblem(filename, self.item_id, self.lcp.get_state())
event_info['new_state']=self.lcp.get_state() event_info['new_state']=self.lcp.get_state()
self.tracker('reset_problem', event_info) self.tracker('reset_problem', event_info)
......
import json import json
## TODO: Abstract out from Django
from django.conf import settings
from mitxmako.shortcuts import render_to_response, render_to_string from mitxmako.shortcuts import render_to_response, render_to_string
from x_module import XModule from x_module import XModule
...@@ -24,13 +22,13 @@ class Module(XModule): ...@@ -24,13 +22,13 @@ class Module(XModule):
textlist=[i for i in textlist if type(i)==str] textlist=[i for i in textlist if type(i)==str]
return "".join(textlist) return "".join(textlist)
try: try:
filename=settings.DATA_DIR+"html/"+self.filename filename="html/"+self.filename
return open(filename).read() return self.filestore.open(filename).read()
except: # For backwards compatibility. TODO: Remove except: # For backwards compatibility. TODO: Remove
return render_to_string(self.filename, {'id': self.item_id}) 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): def __init__(self, system, xml, item_id, state=None):
XModule.__init__(self, xml, item_id, ajax_url, track_url, state, track_function, render_function) XModule.__init__(self, system, xml, item_id, state)
xmltree=etree.fromstring(xml) xmltree=etree.fromstring(xml)
self.filename = None self.filename = None
filename_l=xmltree.xpath("/html/@filename") filename_l=xmltree.xpath("/html/@filename")
......
...@@ -19,6 +19,6 @@ class Module(XModule): ...@@ -19,6 +19,6 @@ class Module(XModule):
def get_html(self): def get_html(self):
return '<input type="hidden" class="schematic" name="{item_id}" height="480" width="640">'.format(item_id=self.item_id) return '<input type="hidden" class="schematic" name="{item_id}" height="480" width="640">'.format(item_id=self.item_id)
def __init__(self, xml, item_id, ajax_url=None, track_url=None, state=None, render_function = None): def __init__(self, system, xml, item_id, state=None):
XModule.__init__(self, xml, item_id, ajax_url, track_url, state, render_function) XModule.__init__(self, system, xml, item_id, state)
...@@ -2,10 +2,7 @@ import json ...@@ -2,10 +2,7 @@ import json
from lxml import etree from lxml import etree
## TODO: Abstract out from Django from mitxmako.shortcuts import render_to_string
from django.http import Http404
from django.conf import settings
from mitxmako.shortcuts import render_to_response, render_to_string
from x_module import XModule from x_module import XModule
...@@ -38,12 +35,10 @@ class Module(XModule): ...@@ -38,12 +35,10 @@ class Module(XModule):
return self.destroy_js return self.destroy_js
def handle_ajax(self, dispatch, get): def handle_ajax(self, dispatch, get):
print "GET", get
print "DISPATCH", dispatch
if dispatch=='goto_position': if dispatch=='goto_position':
self.position = int(get['position']) self.position = int(get['position'])
return json.dumps({'success':True}) return json.dumps({'success':True})
raise Http404() raise self.system.exception404
def render(self): def render(self):
if self.rendered: if self.rendered:
...@@ -107,14 +102,13 @@ class Module(XModule): ...@@ -107,14 +102,13 @@ class Module(XModule):
self.rendered = True self.rendered = True
def __init__(self, system, xml, item_id, state=None):
def __init__(self, xml, item_id, ajax_url=None, track_url=None, state=None, track_function=None, render_function = None): XModule.__init__(self, system, xml, item_id, state)
XModule.__init__(self, xml, item_id, ajax_url, track_url, state, track_function, render_function) self.xmltree = etree.fromstring(xml)
self.xmltree=etree.fromstring(xml)
self.position = 1 self.position = 1
if state!=None: if state != None:
state = json.loads(state) state = json.loads(state)
if 'position' in state: self.position = int(state['position']) if 'position' in state: self.position = int(state['position'])
......
...@@ -14,16 +14,16 @@ class Module(XModule): ...@@ -14,16 +14,16 @@ class Module(XModule):
@classmethod @classmethod
def get_xml_tags(c): def get_xml_tags(c):
## TODO: Abstract out from filesystem
tags = os.listdir(settings.DATA_DIR+'/custom_tags') tags = os.listdir(settings.DATA_DIR+'/custom_tags')
return tags return tags
def get_html(self): def get_html(self):
return self.html return self.html
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, state=None):
XModule.__init__(self, xml, item_id, ajax_url, track_url, state, track_function, render_function) XModule.__init__(self, system, xml, item_id, state)
xmltree = etree.fromstring(xml) xmltree = etree.fromstring(xml)
filename = xmltree.tag filename = xmltree.tag
params = dict(xmltree.items()) params = dict(xmltree.items())
# print params
self.html = render_to_string(filename, params, namespace = 'custom_tags') self.html = render_to_string(filename, params, namespace = 'custom_tags')
import json import json
## TODO: Abstract out from Django
from django.conf import settings
from mitxmako.shortcuts import render_to_response, render_to_string from mitxmako.shortcuts import render_to_response, render_to_string
from x_module import XModule from x_module import XModule
...@@ -26,8 +24,9 @@ class Module(XModule): ...@@ -26,8 +24,9 @@ class Module(XModule):
def get_destroy_js(self): def get_destroy_js(self):
return self.destroy_js_text return self.destroy_js_text
def __init__(self, xml, item_id, ajax_url=None, track_url=None, state=None, track_function=None, render_function = None):
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) xmltree=etree.fromstring(xml)
self.contents=[(e.get("name"),self.render_function(e)) \ self.contents=[(e.get("name"),self.render_function(e)) \
for e in xmltree] for e in xmltree]
......
...@@ -3,8 +3,6 @@ import logging ...@@ -3,8 +3,6 @@ import logging
from lxml import etree 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 mitxmako.shortcuts import render_to_response, render_to_string
from x_module import XModule from x_module import XModule
...@@ -42,7 +40,8 @@ class Module(XModule): ...@@ -42,7 +40,8 @@ class Module(XModule):
return render_to_string('video.html',{'streams':self.video_list(), return render_to_string('video.html',{'streams':self.video_list(),
'id':self.item_id, 'id':self.item_id,
'position':self.position, 'position':self.position,
'name':self.name}) 'name':self.name,
'annotations':self.annotations})
def get_init_js(self): def get_init_js(self):
'''JavaScript code to be run when problem is shown. Be aware '''JavaScript code to be run when problem is shown. Be aware
...@@ -52,19 +51,23 @@ class Module(XModule): ...@@ -52,19 +51,23 @@ class Module(XModule):
log.debug(u"INIT POSITION {0}".format(self.position)) log.debug(u"INIT POSITION {0}".format(self.position))
return render_to_string('video_init.js',{'streams':self.video_list(), return render_to_string('video_init.js',{'streams':self.video_list(),
'id':self.item_id, 'id':self.item_id,
'position':self.position}) 'position':self.position})+self.annotations_init
def get_destroy_js(self): 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): def __init__(self, system, xml, item_id, state=None):
XModule.__init__(self, xml, item_id, ajax_url, track_url, state, track_function, render_function) XModule.__init__(self, system, xml, item_id, state)
self.youtube = etree.XML(xml).get('youtube') xmltree=etree.fromstring(xml)
self.name = etree.XML(xml).get('name') self.youtube = xmltree.get('youtube')
self.name = xmltree.get('name')
self.position = 0 self.position = 0
if state != None: if state != None:
state = json.loads(state) state = json.loads(state)
if 'position' in state: if 'position' in state:
self.position = int(float(state['position'])) 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): ...@@ -45,13 +45,17 @@ class XModule(object):
get is a dictionary-like object ''' get is a dictionary-like object '''
return "" return ""
def __init__(self, xml, item_id, ajax_url=None, track_url=None, state=None, track_function=None, render_function = None): def __init__(self, system, xml, item_id, track_url=None, state=None):
''' In most cases, you must pass state or xml''' ''' In most cases, you must pass state or xml'''
self.xml = xml self.xml = xml
self.item_id = item_id self.item_id = item_id
self.ajax_url = ajax_url
self.track_url = track_url
self.state = state 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 ...@@ -9,7 +9,7 @@ from django.db.models import signals
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from markdown import markdown from markdown import markdown
from settings import * from wiki_settings import *
from util.cache import cache from util.cache import cache
......
...@@ -3,7 +3,7 @@ from django.conf import settings ...@@ -3,7 +3,7 @@ from django.conf import settings
from django.template.defaultfilters import stringfilter from django.template.defaultfilters import stringfilter
from django.utils.http import urlquote as django_urlquote from django.utils.http import urlquote as django_urlquote
from simplewiki.settings import * from simplewiki.wiki_settings import *
register = template.Library() register = template.Library()
......
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import types from django.conf import settings as settings
from django.conf import settings
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.core.context_processors import csrf from django.core.context_processors import csrf
from django.core.urlresolvers import get_callable
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.db.models import Q from django.db.models import Q
from django.http import Http404, HttpResponse, HttpResponseRedirect, HttpResponseServerError, HttpResponseForbidden, HttpResponseNotAllowed from django.http import HttpResponse, HttpResponseRedirect
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.utils import simplejson from django.utils import simplejson
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from mitxmako.shortcuts import render_to_response, render_to_string from mitxmako.shortcuts import render_to_response
from mako.lookup import TemplateLookup
from mako.template import Template from multicourse import multicourse_settings
import mitxmako.middleware
from models import * # TODO: Clean up from models import Revision, Article, CreateArticleForm, RevisionFormWithTitle, RevisionForm
from settings import * import wiki_settings
def view(request, wiki_url): def view(request, wiki_url):
if not request.user.is_authenticated():
return redirect('/')
(article, path, err) = fetch_from_url(request, wiki_url) (article, path, err) = fetch_from_url(request, wiki_url)
if err: if err:
return 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) perm_err = check_permissions(request, article, check_read=True, check_deleted=True)
if perm_err: if perm_err:
return perm_err return perm_err
...@@ -39,15 +32,12 @@ def view(request, wiki_url): ...@@ -39,15 +32,12 @@ def view(request, wiki_url):
'wiki_write': article.can_write_l(request.user), 'wiki_write': article.can_write_l(request.user),
'wiki_attachments_write': article.can_attach(request.user), 'wiki_attachments_write': article.can_attach(request.user),
'wiki_current_revision_deleted' : not (article.current_revision.deleted == 0), '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)) d.update(csrf(request))
return render_to_response('simplewiki_view.html', d) return render_to_response('simplewiki_view.html', d)
def view_revision(request, revision_number, wiki_url, revision=None): 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) (article, path, err) = fetch_from_url(request, wiki_url)
if err: if err:
return err return err
...@@ -76,8 +66,6 @@ def view_revision(request, revision_number, wiki_url, revision=None): ...@@ -76,8 +66,6 @@ def view_revision(request, revision_number, wiki_url, revision=None):
def root_redirect(request): def root_redirect(request):
if not request.user.is_authenticated():
return redirect('/')
try: try:
root = Article.get_root() root = Article.get_root()
except: except:
...@@ -87,8 +75,6 @@ def root_redirect(request): ...@@ -87,8 +75,6 @@ def root_redirect(request):
return HttpResponseRedirect(reverse('wiki_view', args=(root.get_url()))) return HttpResponseRedirect(reverse('wiki_view', args=(root.get_url())))
def create(request, wiki_url): def create(request, wiki_url):
if not request.user.is_authenticated():
return redirect('/')
url_path = get_url_path(wiki_url) url_path = get_url_path(wiki_url)
...@@ -161,9 +147,6 @@ def create(request, wiki_url): ...@@ -161,9 +147,6 @@ def create(request, wiki_url):
return render_to_response('simplewiki_edit.html', d) return render_to_response('simplewiki_edit.html', d)
def edit(request, wiki_url): def edit(request, wiki_url):
if not request.user.is_authenticated():
return redirect('/')
(article, path, err) = fetch_from_url(request, wiki_url) (article, path, err) = fetch_from_url(request, wiki_url)
if err: if err:
return err return err
...@@ -173,7 +156,7 @@ def edit(request, wiki_url): ...@@ -173,7 +156,7 @@ def edit(request, wiki_url):
if perm_err: if perm_err:
return perm_err return perm_err
if WIKI_ALLOW_TITLE_EDIT: if wiki_settings.WIKI_ALLOW_TITLE_EDIT:
EditForm = RevisionFormWithTitle EditForm = RevisionFormWithTitle
else: else:
EditForm = RevisionForm EditForm = RevisionForm
...@@ -195,7 +178,7 @@ def edit(request, wiki_url): ...@@ -195,7 +178,7 @@ def edit(request, wiki_url):
if not request.user.is_anonymous(): if not request.user.is_anonymous():
new_revision.revision_user = request.user new_revision.revision_user = request.user
new_revision.save() 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.title = f.cleaned_data['title']
new_revision.article.save() new_revision.article.save()
return HttpResponseRedirect(reverse('wiki_view', args=(article.get_url(),))) return HttpResponseRedirect(reverse('wiki_view', args=(article.get_url(),)))
...@@ -215,9 +198,6 @@ def edit(request, wiki_url): ...@@ -215,9 +198,6 @@ def edit(request, wiki_url):
return render_to_response('simplewiki_edit.html', d) return render_to_response('simplewiki_edit.html', d)
def history(request, wiki_url, page=1): def history(request, wiki_url, page=1):
if not request.user.is_authenticated():
return redirect('/')
(article, path, err) = fetch_from_url(request, wiki_url) (article, path, err) = fetch_from_url(request, wiki_url)
if err: if err:
return err return err
...@@ -302,9 +282,6 @@ def history(request, wiki_url, page=1): ...@@ -302,9 +282,6 @@ def history(request, wiki_url, page=1):
def revision_feed(request, page=1): def revision_feed(request, page=1):
if not request.user.is_superuser:
return redirect('/')
page_size = 10 page_size = 10
try: try:
...@@ -332,8 +309,6 @@ def revision_feed(request, page=1): ...@@ -332,8 +309,6 @@ def revision_feed(request, page=1):
return render_to_response('simplewiki_revision_feed.html', d) return render_to_response('simplewiki_revision_feed.html', d)
def search_articles(request): def search_articles(request):
if not request.user.is_authenticated():
return redirect('/')
# blampe: We should check for the presence of other popular django search # 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. # 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. # Adding some context to results (eg where matches were) would also be nice.
...@@ -380,9 +355,6 @@ def search_articles(request): ...@@ -380,9 +355,6 @@ def search_articles(request):
def search_add_related(request, wiki_url): def search_add_related(request, wiki_url):
if not request.user.is_authenticated():
return redirect('/')
(article, path, err) = fetch_from_url(request, wiki_url) (article, path, err) = fetch_from_url(request, wiki_url)
if err: if err:
return err return err
...@@ -435,9 +407,6 @@ def add_related(request, wiki_url): ...@@ -435,9 +407,6 @@ def add_related(request, wiki_url):
return HttpResponseRedirect(reverse('wiki_view', args=(article.get_url(),))) return HttpResponseRedirect(reverse('wiki_view', args=(article.get_url(),)))
def remove_related(request, wiki_url, related_id): 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) (article, path, err) = fetch_from_url(request, wiki_url)
if err: if err:
return err return err
...@@ -457,8 +426,6 @@ def remove_related(request, wiki_url, related_id): ...@@ -457,8 +426,6 @@ def remove_related(request, wiki_url, related_id):
return HttpResponseRedirect(reverse('wiki_view', args=(article.get_url(),))) return HttpResponseRedirect(reverse('wiki_view', args=(article.get_url(),)))
def random_article(request): def random_article(request):
if not request.user.is_authenticated():
return redirect('/')
from random import randint from random import randint
num_arts = Article.objects.count() num_arts = Article.objects.count()
article = Article.objects.all()[randint(0, num_arts-1)] article = Article.objects.all()[randint(0, num_arts-1)]
...@@ -470,8 +437,6 @@ def encode_err(request, url): ...@@ -470,8 +437,6 @@ def encode_err(request, url):
return render_to_response('simplewiki_error.html', d) return render_to_response('simplewiki_error.html', d)
def not_found(request, wiki_url): def not_found(request, wiki_url):
if not request.user.is_authenticated():
return redirect('/')
"""Generate a NOT FOUND message for some URL""" """Generate a NOT FOUND message for some URL"""
d = {'wiki_err_notfound': True, d = {'wiki_err_notfound': True,
'wiki_url': wiki_url} 'wiki_url': wiki_url}
...@@ -543,17 +508,22 @@ def check_permissions(request, article, check_read=False, check_write=False, che ...@@ -543,17 +508,22 @@ def check_permissions(request, article, check_read=False, check_write=False, che
# LOGIN PROTECTION # # LOGIN PROTECTION #
#################### ####################
if WIKI_REQUIRE_LOGIN_VIEW: if wiki_settings.WIKI_REQUIRE_LOGIN_VIEW:
view = login_required(view) view = login_required(view)
history = login_required(history) history = login_required(history)
# search_related = login_required(search_related) search_articles = login_required(search_articles)
# wiki_encode_err = login_required(wiki_encode_err) root_redirect = login_required(root_redirect)
revision_feed = login_required(revision_feed)
if WIKI_REQUIRE_LOGIN_EDIT: 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) create = login_required(create)
edit = login_required(edit) edit = login_required(edit)
add_related = login_required(add_related) add_related = login_required(add_related)
remove_related = login_required(remove_related) remove_related = login_required(remove_related)
if WIKI_CONTEXT_PREPROCESSORS: if wiki_settings.WIKI_CONTEXT_PREPROCESSORS:
settings.TEMPLATE_CONTEXT_PROCESSORS = settings.TEMPLATE_CONTEXT_PROCESSORS + WIKI_CONTEXT_PREPROCESSORS settings.TEMPLATE_CONTEXT_PROCESSORS += wiki_settings.WIKI_CONTEXT_PREPROCESSORS
...@@ -3,14 +3,21 @@ import os ...@@ -3,14 +3,21 @@ import os
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.core.servers.basehttp import FileWrapper from django.core.servers.basehttp import FileWrapper
from django.db.models.fields.files import FieldFile 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 django.template import loader, Context
from settings import * # TODO: Clean up from models import ArticleAttachment, get_attachment_filepath
from models import Article, ArticleAttachment, get_attachment_filepath from views import check_permissions, fetch_from_url
from views import not_found, check_permissions, get_url_path, fetch_from_url
from wiki_settings import (
from simplewiki.settings import WIKI_ALLOW_ANON_ATTACHMENTS 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): def add_attachment(request, wiki_url):
......
# Create your views here. from django.contrib.auth.decorators import login_required
import os from mitxmako.shortcuts import render_to_response
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
@login_required
def index(request, page=0): def index(request, page=0):
if not request.user.is_authenticated():
return redirect('/')
return render_to_response('staticbook.html',{'page':int(page)}) return render_to_response('staticbook.html',{'page':int(page)})
def index_shifted(request, page): def index_shifted(request, page):
......
...@@ -10,14 +10,14 @@ from django.conf import settings ...@@ -10,14 +10,14 @@ from django.conf import settings
from django.contrib.auth import logout, authenticate, login from django.contrib.auth import logout, authenticate, login
from django.contrib.auth.forms import PasswordResetForm from django.contrib.auth.forms import PasswordResetForm
from django.contrib.auth.models import User 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.context_processors import csrf
from django.core.mail import send_mail from django.core.mail import send_mail
from django.core.validators import validate_email, validate_slug, ValidationError 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.http import HttpResponse, Http404
from django.shortcuts import redirect from django.shortcuts import redirect
from mitxmako.shortcuts import render_to_response, render_to_string from mitxmako.shortcuts import render_to_response, render_to_string
from mako import exceptions
from django_future.csrf import ensure_csrf_cookie from django_future.csrf import ensure_csrf_cookie
...@@ -93,12 +93,11 @@ def logout_user(request): ...@@ -93,12 +93,11 @@ def logout_user(request):
logout(request) logout(request)
return redirect('/') return redirect('/')
@login_required
@ensure_csrf_cookie @ensure_csrf_cookie
def change_setting(request): def change_setting(request):
''' JSON call to change a profile setting: Right now, location and language ''' 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 up = UserProfile.objects.get(user=request.user) #request.user.profile_cache
if 'location' in request.POST: if 'location' in request.POST:
up.location=request.POST['location'] up.location=request.POST['location']
...@@ -162,15 +161,6 @@ def create_account(request, post_override=None): ...@@ -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'], u=User(username=post_vars['username'],
email=post_vars['email'], email=post_vars['email'],
is_active=False) is_active=False)
...@@ -178,7 +168,20 @@ def create_account(request, post_override=None): ...@@ -178,7 +168,20 @@ def create_account(request, post_override=None):
r=Registration() r=Registration()
# TODO: Rearrange so that if part of the process fails, the whole process fails. # 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 # 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) r.register(u)
up = UserProfile(user=u) up = UserProfile(user=u)
......
...@@ -8,7 +8,8 @@ Common traits: ...@@ -8,7 +8,8 @@ Common traits:
""" """
import json import json
from common import * from envs.logsettings import get_logger_config
from envs.common import *
############################### ALWAYS THE SAME ################################ ############################### ALWAYS THE SAME ################################
DEBUG = False DEBUG = False
...@@ -24,7 +25,6 @@ with open(ENV_ROOT / "env.json") as env_file: ...@@ -24,7 +25,6 @@ with open(ENV_ROOT / "env.json") as env_file:
ENV_TOKENS = json.load(env_file) ENV_TOKENS = json.load(env_file)
SITE_NAME = ENV_TOKENS['SITE_NAME'] SITE_NAME = ENV_TOKENS['SITE_NAME']
CSRF_COOKIE_DOMAIN = ENV_TOKENS['CSRF_COOKIE_DOMAIN']
BOOK_URL = ENV_TOKENS['BOOK_URL'] BOOK_URL = ENV_TOKENS['BOOK_URL']
MEDIA_URL = ENV_TOKENS['MEDIA_URL'] MEDIA_URL = ENV_TOKENS['MEDIA_URL']
...@@ -32,10 +32,10 @@ LOG_DIR = ENV_TOKENS['LOG_DIR'] ...@@ -32,10 +32,10 @@ LOG_DIR = ENV_TOKENS['LOG_DIR']
CACHES = ENV_TOKENS['CACHES'] CACHES = ENV_TOKENS['CACHES']
LOGGING = logsettings.get_logger_config(LOG_DIR, LOGGING = get_logger_config(LOG_DIR,
logging_env=ENV_TOKENS['LOGGING_ENV'], logging_env=ENV_TOKENS['LOGGING_ENV'],
syslog_addr=(ENV_TOKENS['SYSLOG_SERVER'], 514), syslog_addr=(ENV_TOKENS['SYSLOG_SERVER'], 514),
debug=False) debug=False)
############################## SECURE AUTH ITEMS ############################### ############################## SECURE AUTH ITEMS ###############################
# Secret things: passwords, access keys, etc. # Secret things: passwords, access keys, etc.
...@@ -47,4 +47,4 @@ SECRET_KEY = AUTH_TOKENS['SECRET_KEY'] ...@@ -47,4 +47,4 @@ SECRET_KEY = AUTH_TOKENS['SECRET_KEY']
AWS_ACCESS_KEY_ID = AUTH_TOKENS["AWS_ACCESS_KEY_ID"] AWS_ACCESS_KEY_ID = AUTH_TOKENS["AWS_ACCESS_KEY_ID"]
AWS_SECRET_ACCESS_KEY = AUTH_TOKENS["AWS_SECRET_ACCESS_KEY"] AWS_SECRET_ACCESS_KEY = AUTH_TOKENS["AWS_SECRET_ACCESS_KEY"]
DATABASES = AUTH_TOKENS['DATABASES'] DATABASES = AUTH_TOKENS['DATABASES']
\ No newline at end of file
...@@ -24,8 +24,7 @@ import tempfile ...@@ -24,8 +24,7 @@ import tempfile
import djcelery import djcelery
from path import path from path import path
from askbotsettings import * # this is where LIVESETTINGS_OPTIONS comes from from envs.askbotsettings import * # this is where LIVESETTINGS_OPTIONS comes from
import logsettings
################################### FEATURES ################################### ################################### FEATURES ###################################
COURSEWARE_ENABLED = True COURSEWARE_ENABLED = True
...@@ -81,6 +80,7 @@ TEMPLATE_DIRS = ( ...@@ -81,6 +80,7 @@ TEMPLATE_DIRS = (
TEMPLATE_CONTEXT_PROCESSORS = ( TEMPLATE_CONTEXT_PROCESSORS = (
'django.core.context_processors.request', 'django.core.context_processors.request',
'askbot.context.application_settings', 'askbot.context.application_settings',
'django.contrib.messages.context_processors.messages',
#'django.core.context_processors.i18n', #'django.core.context_processors.i18n',
'askbot.user_messages.context_processors.user_messages',#must be before auth 'askbot.user_messages.context_processors.user_messages',#must be before auth
'django.core.context_processors.auth', #this is required for admin 'django.core.context_processors.auth', #this is required for admin
...@@ -113,7 +113,6 @@ TEMPLATE_DEBUG = False ...@@ -113,7 +113,6 @@ TEMPLATE_DEBUG = False
# Site info # Site info
SITE_ID = 1 SITE_ID = 1
SITE_NAME = "localhost:8000" SITE_NAME = "localhost:8000"
CSRF_COOKIE_DOMAIN = '127.0.0.1'
HTTPS = 'on' HTTPS = 'on'
ROOT_URLCONF = 'mitx.urls' ROOT_URLCONF = 'mitx.urls'
IGNORABLE_404_ENDS = ('favicon.ico') IGNORABLE_404_ENDS = ('favicon.ico')
...@@ -134,7 +133,7 @@ STATIC_ROOT = ENV_ROOT / "staticfiles" # We don't run collectstatic -- this is t ...@@ -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 # FIXME: We should iterate through the courses we have, adding the static
# contents for each of them. (Right now we just use symlinks.) # contents for each of them. (Right now we just use symlinks.)
STATICFILES_DIRS = ( STATICFILES_DIRS = [
PROJECT_ROOT / "static", PROJECT_ROOT / "static",
ASKBOT_ROOT / "askbot" / "skins", ASKBOT_ROOT / "askbot" / "skins",
("circuits", DATA_DIR / "images"), ("circuits", DATA_DIR / "images"),
...@@ -143,7 +142,7 @@ STATICFILES_DIRS = ( ...@@ -143,7 +142,7 @@ STATICFILES_DIRS = (
# This is how you would use the textbook images locally # This is how you would use the textbook images locally
# ("book", ENV_ROOT / "book_images") # ("book", ENV_ROOT / "book_images")
) ]
# Locale/Internationalization # Locale/Internationalization
TIME_ZONE = 'America/New_York' # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name 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 ...@@ -151,6 +150,9 @@ LANGUAGE_CODE = 'en' # http://www.i18nguy.com/unicode/language-identifiers.html
USE_I18N = True USE_I18N = True
USE_L10N = True USE_L10N = True
# Messages
MESSAGE_STORAGE = 'django.contrib.messages.storage.session.SessionStorage'
#################################### AWS ####################################### #################################### AWS #######################################
# S3BotoStorage insists on a timeout for uploaded assets. We should make it # S3BotoStorage insists on a timeout for uploaded assets. We should make it
# permanent instead, but rather than trying to figure out exactly where that # permanent instead, but rather than trying to figure out exactly where that
...@@ -179,8 +181,8 @@ CELERY_ALWAYS_EAGER = True ...@@ -179,8 +181,8 @@ CELERY_ALWAYS_EAGER = True
djcelery.setup_loader() djcelery.setup_loader()
################################# SIMPLEWIKI ################################### ################################# SIMPLEWIKI ###################################
WIKI_REQUIRE_LOGIN_EDIT = True SIMPLE_WIKI_REQUIRE_LOGIN_EDIT = True
WIKI_REQUIRE_LOGIN_VIEW = True SIMPLE_WIKI_REQUIRE_LOGIN_VIEW = False
################################# Middleware ################################### ################################# Middleware ###################################
# List of finder classes that know how to find static files in # List of finder classes that know how to find static files in
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
These are debug machines used for content creators, so they're kind of a cross These are debug machines used for content creators, so they're kind of a cross
between dev machines and AWS machines. between dev machines and AWS machines.
""" """
from aws import * from envs.aws import *
DEBUG = True DEBUG = True
TEMPLATE_DEBUG = True TEMPLATE_DEBUG = True
......
...@@ -7,16 +7,17 @@ sessions. Assumes structure: ...@@ -7,16 +7,17 @@ sessions. Assumes structure:
/mitx # The location of this repo /mitx # The location of this repo
/log # Where we're going to write log files /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 DEBUG = True
PIPELINE = True PIPELINE = True
TEMPLATE_DEBUG = True TEMPLATE_DEBUG = True
LOGGING = logsettings.get_logger_config(ENV_ROOT / "log", LOGGING = get_logger_config(ENV_ROOT / "log",
logging_env="dev", logging_env="dev",
tracking_filename="tracking.log", tracking_filename="tracking.log",
debug=True) debug=True)
DATABASES = { DATABASES = {
'default': { 'default': {
...@@ -74,7 +75,8 @@ DEBUG_TOOLBAR_PANELS = ( ...@@ -74,7 +75,8 @@ DEBUG_TOOLBAR_PANELS = (
############################ FILE UPLOADS (ASKBOT) ############################# ############################ FILE UPLOADS (ASKBOT) #############################
DEFAULT_FILE_STORAGE = 'django.core.files.storage.FileSystemStorage' DEFAULT_FILE_STORAGE = 'django.core.files.storage.FileSystemStorage'
MEDIA_ROOT = ENV_ROOT / "uploads" 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_TEMP_DIR = ENV_ROOT / "uploads"
FILE_UPLOAD_HANDLERS = ( FILE_UPLOAD_HANDLERS = (
'django.core.files.uploadhandler.MemoryFileUploadHandler', 'django.core.files.uploadhandler.MemoryFileUploadHandler',
......
...@@ -13,7 +13,7 @@ Dir structure: ...@@ -13,7 +13,7 @@ Dir structure:
/log # Where we're going to write log files /log # Where we're going to write log files
""" """
from dev import * from envs.dev import *
DATABASES = { DATABASES = {
'default': { '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'): ...@@ -27,12 +27,16 @@ def render_to_string(template_name, dictionary, context=None, namespace='main'):
# collapse context_instance to a single dictionary for mako # collapse context_instance to a single dictionary for mako
context_dictionary = {} context_dictionary = {}
context_instance['settings'] = settings context_instance['settings'] = settings
context_instance['MITX_ROOT_URL'] = settings.MITX_ROOT_URL
for d in mitxmako.middleware.requestcontext: for d in mitxmako.middleware.requestcontext:
context_dictionary.update(d) context_dictionary.update(d)
for d in context_instance: for d in context_instance:
context_dictionary.update(d) context_dictionary.update(d)
if context: if context:
context_dictionary.update(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 # fetch and render template
template = middleware.lookup[namespace].get_template(template_name) template = middleware.lookup[namespace].get_template(template_name)
return template.render(**context_dictionary) 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 ...@@ -3,7 +3,6 @@ import json
import sys import sys
from django.conf import settings from django.conf import settings
from django.conf import settings
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.core.context_processors import csrf from django.core.context_processors import csrf
from django.core.mail import send_mail from django.core.mail import send_mail
...@@ -60,7 +59,10 @@ def send_feedback(request): ...@@ -60,7 +59,10 @@ def send_feedback(request):
def info(request): def info(request):
''' Info page (link from main header) ''' ''' Info page (link from main header) '''
if not request.user.is_authenticated():
return redirect('/')
return render_to_response("info.html", {}) return render_to_response("info.html", {})
def mitxhome(request):
''' Home page (link from main header). List of courses. '''
if settings.ENABLE_MULTICOURSE:
return render_to_response("mitxhome.html", {})
return info(request)
...@@ -4,14 +4,15 @@ require 'tempfile' ...@@ -4,14 +4,15 @@ require 'tempfile'
# Build Constants # Build Constants
REPO_ROOT = File.dirname(__FILE__) REPO_ROOT = File.dirname(__FILE__)
BUILD_DIR = File.join(REPO_ROOT, "build") BUILD_DIR = File.join(REPO_ROOT, "build")
REPORT_DIR = File.join(REPO_ROOT, "reports")
# Packaging constants # Packaging constants
DEPLOY_DIR = "/opt/wwc" DEPLOY_DIR = "/opt/wwc"
PACKAGE_NAME = "mitx" PACKAGE_NAME = "mitx"
LINK_PATH = "/opt/wwc/mitx" LINK_PATH = "/opt/wwc/mitx"
VERSION = "0.1" PKG_VERSION = "0.1"
COMMIT = (ENV["GIT_COMMIT"] || `git rev-parse HEAD`).chomp()[0, 10] 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() BUILD_NUMBER = (ENV["BUILD_NUMBER"] || "dev").chomp()
if BRANCH == "master" if BRANCH == "master"
...@@ -19,14 +20,41 @@ if BRANCH == "master" ...@@ -19,14 +20,41 @@ if BRANCH == "master"
else else
DEPLOY_NAME = "#{PACKAGE_NAME}-#{BRANCH}-#{BUILD_NUMBER}-#{COMMIT}" DEPLOY_NAME = "#{PACKAGE_NAME}-#{BRANCH}-#{BUILD_NUMBER}-#{COMMIT}"
end end
INSTALL_DIR_PATH = File.join(DEPLOY_DIR, DEPLOY_NAME)
PACKAGE_REPO = "packages@gp.mitx.mit.edu:/opt/pkgrepo.incoming" 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 # 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") 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 task :package do
FileUtils.mkdir_p(BUILD_DIR) FileUtils.mkdir_p(BUILD_DIR)
...@@ -53,10 +81,15 @@ task :package do ...@@ -53,10 +81,15 @@ task :package do
args = ["fakeroot", "fpm", "-s", "dir", "-t", "deb", args = ["fakeroot", "fpm", "-s", "dir", "-t", "deb",
"--after-install=#{postinstall.path}", "--after-install=#{postinstall.path}",
"--prefix=#{INSTALL_DIR_PATH}", "--prefix=#{INSTALL_DIR_PATH}",
"--exclude=build",
"--exclude=rakefile",
"--exclude=.git",
"--exclude=**/*.pyc",
"--exclude=reports",
"-C", "#{REPO_ROOT}", "-C", "#{REPO_ROOT}",
"--provides=#{PACKAGE_NAME}", "--provides=#{PACKAGE_NAME}",
"--name=#{DEPLOY_NAME}", "--name=#{NORMALIZED_DEPLOY_NAME}",
"--version=#{VERSION}", "--version=#{PKG_VERSION}",
"-a", "all", "-a", "all",
"."] "."]
system(*args) || raise("fpm failed to build the .deb") system(*args) || raise("fpm failed to build the .deb")
...@@ -64,5 +97,5 @@ task :package do ...@@ -64,5 +97,5 @@ task :package do
end end
task :publish => :package do 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 end
...@@ -2,10 +2,10 @@ section.help.main-content { ...@@ -2,10 +2,10 @@ section.help.main-content {
padding: lh(); padding: lh();
h1 { h1 {
margin-top: 0; border-bottom: 1px solid #ddd;
margin-bottom: lh(); margin-bottom: lh();
margin-top: 0;
padding-bottom: lh(); padding-bottom: lh();
border-bottom: 1px solid #ddd;
} }
p { p {
...@@ -17,9 +17,9 @@ section.help.main-content { ...@@ -17,9 +17,9 @@ section.help.main-content {
} }
section.self-help { section.self-help {
float: left;
margin-bottom: lh(); margin-bottom: lh();
margin-right: flex-gutter(); margin-right: flex-gutter();
float: left;
width: flex-grid(6); width: flex-grid(6);
ul { ul {
...@@ -36,17 +36,17 @@ section.help.main-content { ...@@ -36,17 +36,17 @@ section.help.main-content {
width: flex-grid(6); width: flex-grid(6);
dl { dl {
margin-bottom: lh();
display: block; display: block;
margin-bottom: lh();
dd { dd {
margin-bottom: lh(); margin-bottom: lh();
} }
dt { dt {
font-weight: bold;
float: left;
clear: left; clear: left;
float: left;
font-weight: bold;
width: flex-grid(2, 6); width: flex-grid(2, 6);
} }
} }
......
...@@ -16,28 +16,28 @@ div.info-wrapper { ...@@ -16,28 +16,28 @@ div.info-wrapper {
list-style: none; list-style: none;
> li { > li {
padding-bottom: lh(.5);
margin-bottom: lh(.5);
@extend .clearfix; @extend .clearfix;
border-bottom: 1px solid #e3e3e3; border-bottom: 1px solid #e3e3e3;
margin-bottom: lh(.5);
padding-bottom: lh(.5);
&:first-child { &:first-child {
padding: lh(.5);
margin: 0 (-(lh(.5))) lh();
background: $cream; background: $cream;
border-bottom: 1px solid darken($cream, 10%); border-bottom: 1px solid darken($cream, 10%);
margin: 0 (-(lh(.5))) lh();
padding: lh(.5);
} }
h2 { h2 {
float: left; float: left;
width: flex-grid(2, 9);
margin: 0 flex-gutter() 0 0; margin: 0 flex-gutter() 0 0;
width: flex-grid(2, 9);
} }
section.update-description { section.update-description {
float: left; float: left;
width: flex-grid(7, 9);
margin-bottom: 0; margin-bottom: 0;
width: flex-grid(7, 9);
li { li {
margin-bottom: lh(.5); margin-bottom: lh(.5);
...@@ -55,9 +55,9 @@ div.info-wrapper { ...@@ -55,9 +55,9 @@ div.info-wrapper {
section.handouts { section.handouts {
@extend .sidebar; @extend .sidebar;
border-left: 1px solid #d3d3d3;
@include border-radius(0 4px 4px 0); @include border-radius(0 4px 4px 0);
border-right: 0; border-right: 0;
border-left: 1px solid #d3d3d3;
header { header {
@extend .bottom-border; @extend .bottom-border;
...@@ -69,32 +69,32 @@ div.info-wrapper { ...@@ -69,32 +69,32 @@ div.info-wrapper {
} }
p { p {
color: #666;
font-size: 12px;
margin-bottom: 0; margin-bottom: 0;
margin-top: 4px; margin-top: 4px;
font-size: 12px;
color: #666;
} }
} }
ol { ol {
list-style: none;
background: none; background: none;
list-style: none;
li { li {
@include box-shadow(0 1px 0 #eee); @extend .clearfix;
background: none;
border-bottom: 1px solid #d3d3d3; border-bottom: 1px solid #d3d3d3;
@include box-shadow(0 1px 0 #eee);
@include box-sizing(border-box); @include box-sizing(border-box);
@extend .clearfix;
padding: 7px lh(.75); padding: 7px lh(.75);
background: none;
position: relative; position: relative;
&.expandable, &.expandable,
&.collapsable { &.collapsable {
h4 { h4 {
padding-left: 18px;
font-style: $body-font-size; font-style: $body-font-size;
font-weight: normal; font-weight: normal;
padding-left: 18px;
} }
} }
...@@ -103,10 +103,10 @@ div.info-wrapper { ...@@ -103,10 +103,10 @@ div.info-wrapper {
margin: 7px (-(lh(.75))) 0; margin: 7px (-(lh(.75))) 0;
li { li {
padding-left: 18px + lh(.75);
@include box-shadow(inset 0 1px 0 #eee);
border-top: 1px solid #d3d3d3;
border-bottom: 0; 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 { ...@@ -116,13 +116,13 @@ div.info-wrapper {
div.hitarea { div.hitarea {
background-image: url('../images/treeview-default.gif'); background-image: url('../images/treeview-default.gif');
width: 100%;
height: 100%;
max-height: 20px;
display: block; display: block;
position: absolute; height: 100%;
left: lh(.75); left: lh(.75);
margin-left: 0; margin-left: 0;
max-height: 20px;
position: absolute;
width: 100%;
&:hover { &:hover {
opacity: 0.6; opacity: 0.6;
...@@ -140,27 +140,27 @@ div.info-wrapper { ...@@ -140,27 +140,27 @@ div.info-wrapper {
h3 { h3 {
border-bottom: 0; border-bottom: 0;
text-transform: uppercase;
font-weight: bold;
color: #999;
@include box-shadow(none); @include box-shadow(none);
color: #999;
font-size: 12px; font-size: 12px;
font-weight: bold;
text-transform: uppercase;
} }
p { p {
font-size: $body-font-size;
letter-spacing: 0;
margin: 0; margin: 0;
text-transform: none; text-transform: none;
letter-spacing: 0;
font-size: $body-font-size;
a { a {
padding-right: 8px; padding-right: 8px;
&:before { &:before {
color: #ccc;
content: "•"; content: "•";
@include inline-block(); @include inline-block();
padding-right: 8px; padding-right: 8px;
color: #ccc;
} }
&:first-child { &:first-child {
...@@ -173,10 +173,10 @@ div.info-wrapper { ...@@ -173,10 +173,10 @@ div.info-wrapper {
} }
a { a {
@include transition();
color: lighten($text-color, 10%); color: lighten($text-color, 10%);
text-decoration: none;
@include inline-block(); @include inline-block();
text-decoration: none;
@include transition();
&:hover { &:hover {
color: $mit-red; color: $mit-red;
......
...@@ -4,14 +4,14 @@ div.profile-wrapper { ...@@ -4,14 +4,14 @@ div.profile-wrapper {
section.user-info { section.user-info {
@extend .sidebar; @extend .sidebar;
@include border-radius(0px 4px 4px 0);
border-left: 1px solid #d3d3d3; border-left: 1px solid #d3d3d3;
@include border-radius(0px 4px 4px 0);
border-right: 0; border-right: 0;
header { header {
padding: lh(.5) lh();
margin: 0 ;
@extend .bottom-border; @extend .bottom-border;
margin: 0 ;
padding: lh(.5) lh();
h1 { h1 {
font-size: 18px; font-size: 18px;
...@@ -20,12 +20,12 @@ div.profile-wrapper { ...@@ -20,12 +20,12 @@ div.profile-wrapper {
} }
a { a {
color: #999;
font-size: 12px;
position: absolute; position: absolute;
top: 13px;
right: lh(.5); right: lh(.5);
text-transform: uppercase; text-transform: uppercase;
font-size: 12px; top: 13px;
color: #999;
&:hover { &:hover {
color: #555; color: #555;
...@@ -37,14 +37,14 @@ div.profile-wrapper { ...@@ -37,14 +37,14 @@ div.profile-wrapper {
list-style: none; list-style: none;
li { li {
@include transition(); border-bottom: 1px solid #d3d3d3;
@include box-shadow(0 1px 0 #eee);
color: lighten($text-color, 10%); color: lighten($text-color, 10%);
display: block; display: block;
text-decoration: none;
@include box-shadow(0 1px 0 #eee);
padding: 7px lh(); padding: 7px lh();
border-bottom: 1px solid #d3d3d3;
position: relative; position: relative;
text-decoration: none;
@include transition();
div#location_sub, div#language_sub { div#location_sub, div#language_sub {
font-weight: bold; font-weight: bold;
...@@ -57,9 +57,9 @@ div.profile-wrapper { ...@@ -57,9 +57,9 @@ div.profile-wrapper {
input { input {
&[type="text"] { &[type="text"] {
@include box-sizing(border-box);
margin: lh(.5) 0; margin: lh(.5) 0;
width: 100%; width: 100%;
@include box-sizing(border-box);
} }
&[type="input"]{ &[type="input"]{
...@@ -80,12 +80,12 @@ div.profile-wrapper { ...@@ -80,12 +80,12 @@ div.profile-wrapper {
a.edit-email, a.edit-email,
a.name-edit, a.name-edit,
a.email-edit { a.email-edit {
color: #999;
font-size: 12px;
position: absolute; position: absolute;
top: 9px;
right: lh(.5); right: lh(.5);
text-transform: uppercase; text-transform: uppercase;
font-size: 12px; top: 9px;
color: #999;
&:hover { &:hover {
color: #555; color: #555;
...@@ -93,10 +93,10 @@ div.profile-wrapper { ...@@ -93,10 +93,10 @@ div.profile-wrapper {
} }
p { p {
color: #999;
font-size: 12px; font-size: 12px;
margin-bottom: 0; margin-bottom: 0;
margin-top: 4px; margin-top: 4px;
color: #999;
} }
a.deactivate { a.deactivate {
...@@ -132,10 +132,10 @@ div.profile-wrapper { ...@@ -132,10 +132,10 @@ div.profile-wrapper {
padding: 7px lh(); padding: 7px lh();
h2 { h2 {
margin-top: 0; font-size: $body-font-size;
font-weight: bold; font-weight: bold;
margin-top: 0;
text-transform: uppercase; text-transform: uppercase;
font-size: $body-font-size;
} }
} }
} }
...@@ -148,14 +148,14 @@ div.profile-wrapper { ...@@ -148,14 +148,14 @@ div.profile-wrapper {
@extend .clearfix; @extend .clearfix;
h1 { h1 {
margin: 0;
float: left; float: left;
margin: 0;
} }
} }
div#grade-detail-graph { div#grade-detail-graph {
width: 100%;
min-height: 300px; min-height: 300px;
width: 100%;
} }
> ol { > ol {
......
...@@ -3,8 +3,8 @@ div.book-wrapper { ...@@ -3,8 +3,8 @@ div.book-wrapper {
section.book-sidebar { section.book-sidebar {
@extend .sidebar; @extend .sidebar;
@include box-sizing(border-box);
@extend .tran; @extend .tran;
@include box-sizing(border-box);
ul#booknav { ul#booknav {
font-size: 12px; font-size: 12px;
...@@ -22,14 +22,14 @@ div.book-wrapper { ...@@ -22,14 +22,14 @@ div.book-wrapper {
padding-left: 30px; padding-left: 30px;
div.hitarea { div.hitarea {
margin-left: -22px;
background-image: url('../images/treeview-default.gif'); background-image: url('../images/treeview-default.gif');
margin-left: -22px;
position: relative; position: relative;
top: 4px; top: 4px;
&:hover { &:hover {
opacity: 0.6;
filter: alpha(opacity=60); filter: alpha(opacity=60);
opacity: 0.6;
} }
} }
...@@ -63,13 +63,13 @@ div.book-wrapper { ...@@ -63,13 +63,13 @@ div.book-wrapper {
li { li {
&.last { &.last {
float: left;
display: block; display: block;
float: left;
a { a {
@include box-shadow(inset -1px 0 0 lighten(#f6efd4, 5%));
border-right: 1px solid darken(#f6efd4, 20%);
border-left: 0; 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 { ...@@ -81,10 +81,10 @@ div.book-wrapper {
} }
&.bottom-nav { &.bottom-nav {
margin-top: lh();
margin-bottom: -(lh());
border-bottom: 0; border-bottom: 0;
border-top: 1px solid #EDDFAA; border-top: 1px solid #EDDFAA;
margin-bottom: -(lh());
margin-top: lh();
} }
} }
...@@ -110,18 +110,18 @@ div.book-wrapper { ...@@ -110,18 +110,18 @@ div.book-wrapper {
} }
h2 { h2 {
padding: 0;
visibility: hidden; visibility: hidden;
width: 10px; width: 10px;
padding: 0;
} }
} }
ul#booknav { ul#booknav {
max-height: 100px;
overflow: hidden;
padding: 0;
visibility: hidden; visibility: hidden;
width: 10px; width: 10px;
padding: 0;
overflow: hidden;
max-height: 100px;
} }
} }
......
...@@ -35,10 +35,10 @@ img { ...@@ -35,10 +35,10 @@ img {
} }
#{$all-text-inputs}, textarea { #{$all-text-inputs}, textarea {
@include box-shadow(0 -1px 0 #fff);
@include linear-gradient(#eee, #fff);
border: 1px solid #999; border: 1px solid #999;
@include box-shadow(0 -1px 0 #fff);
font: $body-font-size $body-font-family; font: $body-font-size $body-font-family;
@include linear-gradient(#eee, #fff);
padding: 4px; padding: 4px;
&:focus { &:focus {
...@@ -63,7 +63,7 @@ a { ...@@ -63,7 +63,7 @@ a {
p &, li > &, span > &, .inline { p &, li > &, span > &, .inline {
border-bottom: 1px solid #bbb; border-bottom: 1px solid #bbb;
font-style: italic; // font-style: italic;
} }
&:hover, &:focus { &:hover, &:focus {
......
.clearfix:after { .clearfix:after {
clear: both;
content: "."; content: ".";
display: block; display: block;
height: 0; height: 0;
clear: both;
visibility: hidden; visibility: hidden;
} }
...@@ -40,27 +40,27 @@ h1.top-header { ...@@ -40,27 +40,27 @@ h1.top-header {
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
&:hover, &:focus { &:hover, &:focus {
border: 1px solid darken(#888, 20%);
@include box-shadow(inset 0 1px 0 lighten(#888, 20%), 0 0 3px #ccc); @include box-shadow(inset 0 1px 0 lighten(#888, 20%), 0 0 3px #ccc);
@include linear-gradient(lighten(#888, 10%), darken(#888, 5%)); @include linear-gradient(lighten(#888, 10%), darken(#888, 5%));
border: 1px solid darken(#888, 20%);
} }
} }
.light-button, a.light-button { .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; border: 1px solid #ccc;
padding: 4px 8px; @include border-radius(3px);
@include box-shadow(inset 0 1px 0 #fff);
color: #666; color: #666;
cursor: pointer;
font: normal $body-font-size $body-font-family; font: normal $body-font-size $body-font-family;
@include linear-gradient(#fff, lighten(#888, 40%));
padding: 4px 8px;
text-decoration: none; text-decoration: none;
cursor: pointer;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
&:hover, &:focus { &:hover, &:focus {
@include linear-gradient(#fff, lighten(#888, 37%));
border: 1px solid #ccc; border: 1px solid #ccc;
@include linear-gradient(#fff, lighten(#888, 37%));
text-decoration: none; text-decoration: none;
} }
} }
...@@ -70,8 +70,8 @@ h1.top-header { ...@@ -70,8 +70,8 @@ h1.top-header {
color: $mit-red; color: $mit-red;
&:hover { &:hover {
text-decoration: none;
color: darken($mit-red, 20%); color: darken($mit-red, 20%);
text-decoration: none;
} }
} }
} }
...@@ -110,13 +110,13 @@ h1.top-header { ...@@ -110,13 +110,13 @@ h1.top-header {
} }
a { a {
font-style: normal;
border: none; border: none;
font-style: normal;
} }
.bottom-border { .bottom-border {
@include box-shadow(0 1px 0 #eee);
border-bottom: 1px solid #d3d3d3; border-bottom: 1px solid #d3d3d3;
@include box-shadow(0 1px 0 #eee);
} }
@media print { @media print {
...@@ -124,10 +124,10 @@ h1.top-header { ...@@ -124,10 +124,10 @@ h1.top-header {
} }
h3 { h3 {
border: none;
border-bottom: 1px solid #d3d3d3;
@extend .bottom-border; @extend .bottom-border;
background: none; background: none;
border: none;
border-bottom: 1px solid #d3d3d3;
color: #000; color: #000;
font-weight: normal; font-weight: normal;
margin: 0; margin: 0;
...@@ -172,8 +172,8 @@ h1.top-header { ...@@ -172,8 +172,8 @@ h1.top-header {
position: relative; position: relative;
h2 { h2 {
padding-right: 20px;
margin: 0; margin: 0;
padding-right: 20px;
} }
a { a {
...@@ -205,10 +205,10 @@ h1.top-header { ...@@ -205,10 +205,10 @@ h1.top-header {
border-bottom: 1px solid darken($cream, 10%); border-bottom: 1px solid darken($cream, 10%);
@include box-shadow(inset 0 1px 0 #fff, inset 1px 0 0 #fff); @include box-shadow(inset 0 1px 0 #fff, inset 1px 0 0 #fff);
font-size: 12px; font-size: 12px;
height:46px;
line-height: 46px;
margin: (-$body-line-height) (-$body-line-height) $body-line-height; margin: (-$body-line-height) (-$body-line-height) $body-line-height;
text-shadow: 0 1px 0 #fff; text-shadow: 0 1px 0 #fff;
line-height: 46px;
height:46px;
@media print { @media print {
display: none; display: none;
...@@ -242,10 +242,10 @@ h1.top-header { ...@@ -242,10 +242,10 @@ h1.top-header {
} }
p.ie-warning { p.ie-warning {
background: yellow;
display: block !important; display: block !important;
line-height: 1.3em; line-height: 1.3em;
background: yellow; margin-bottom: 0;
padding: lh(); padding: lh();
text-align: left; 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 // Line-height
@function lh($amount: 1) { @function lh($amount: 1) {
@return $body-line-height * $amount; @return $body-line-height * $amount;
......
// Variables // Variables
// ---------------------------------------- // // ---------------------------------------- //
// fonts // Type
$body-font-family: "Open Sans", "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Geneva, Verdana, sans-serif;; $body-font-family: "Open Sans", "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Geneva, Verdana, sans-serif;
$body-font-size: 14px; $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); $body-line-height: golden-ratio($body-font-size, 1);
//Flexible grid // Grid
$fg-column: $column-width; $fg-column: 80px;
$fg-gutter: $gutter-width; $fg-gutter: 25px;
$fg-max-columns: $columns; $fg-max-columns: 12;
$fg-max-width: 1400px; $fg-max-width: 1400px;
$fg-min-width: 810px; $fg-min-width: 810px;
// color // Color
$light-gray: #ddd; $light-gray: #ddd;
$dark-gray: #333; $dark-gray: #333;
$mit-red: #993333; $mit-red: #993333;
$cream: #F6EFD4; $cream: #F6EFD4;
$text-color: $dark-gray; $text-color: $dark-gray;
$border-color: $light-gray; $border-color: $light-gray;
// JM MOSFET AMPLIFIER // JM MOSFET AMPLIFIER
div#graph-container { section.tool-wrapper {
@extend .clearfix; @extend .clearfix;
border-top: 1px solid #ddd; background: #073642;
padding-top: lh(1.0); border-bottom: 1px solid darken(#002b36, 10%);
border-top: 1px solid darken(#002b36, 10%);
canvas#graph { @include box-shadow(inset 0 0 0 4px darken(#094959, 2%));
width: flex-grid(4.5, 9); color: #839496;
float: left; display: table;
margin-right: flex-gutter(9); margin: lh() (-(lh())) 0;
}
div#graph-container {
div.graph-controls { background: none;
width: flex-grid(4.5, 9); @include box-sizing(border-box);
float: left; display: table-cell;
padding: lh();
select#musicTypeSelect { vertical-align: top;
display: block; width: flex-grid(4.5, 9) + flex-gutter(9);
margin-bottom: lh();
.ui-widget-content {
background: none;
border: none;
@include border-radius(0);
} }
div#graph-output { canvas {
display: block; width: 100%;
margin-bottom: lh();
} }
div#graph-listen { ul.ui-tabs-nav {
display: block; background: darken(#073642, 2%);
margin-bottom: lh(); border-bottom: 1px solid darken(#073642, 8%);
} @include border-radius(0);
margin: (-(lh())) (-(lh())) 0;
p { padding: 0;
margin-bottom: lh(.5); position: relative;
} width: 110%;
div#label { li {
display: inline-block; background: none;
} border: none;
@include border-radius(0);
input#playButton { color: #fff;
display: block; 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 { div#controlls-container {
@extend .clearfix; @extend .clearfix;
background: darken(#073642, 2%);
canvas { 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); width: flex-grid(4.5, 9);
float: left;
margin-right: flex-gutter(9);
}
div.schematic-sliders { div.graph-controls {
width: flex-grid(4.5, 9);
float: left; div.music-wrapper {
@extend .clearfix;
div.slider-label#vs { border-bottom: 1px solid darken(#073642, 10%);
margin-top: lh(2.0); @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 { label {
margin-bottom: lh(0.5); @include border-radius(2px);
color: #fff;
font-weight: bold;
padding: 3px;
-webkit-font-smoothing: antialiased;
} }
div.slider { //MOSFET AMPLIFIER
margin-bottom: lh(1); label[for="vinCheckbox"], label[for="vinRadioButton"]{
color: desaturate(#00bfff, 50%);
} }
}
}
//End JM MOSFET AMPLIFIER
// Labels
div.graph-controls, div#graph-listen {
label { label[for="voutCheckbox"], label[for="voutRadioButton"]{
@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"]{
color: darken(#ffcf48, 20%); color: darken(#ffcf48, 20%);
} }
label[for="vrCheckbox"], label[for="vrRadioButton"]{
label[for="vrCheckbox"], label[for="vrRadioButton"]{
color: desaturate(#1df914, 40%); color: desaturate(#1df914, 40%);
} }
//RC Filters
label[for="vcCheckbox"], label[for="vcRadioButton"]{ //RC Filters
label[for="vcCheckbox"], label[for="vcRadioButton"]{
color: darken(#ffcf48, 20%); color: darken(#ffcf48, 20%);
} }
//RLC Series
label[for="vlCheckbox"], label[for="vlRadioButton"]{ //RLC Series
label[for="vlCheckbox"], label[for="vlRadioButton"]{
color: desaturate(#d33682, 40%); 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 { div.course-wrapper {
@extend .table-wrapper; @extend .table-wrapper;
...@@ -7,9 +17,12 @@ div.course-wrapper { ...@@ -7,9 +17,12 @@ div.course-wrapper {
section.course-content { section.course-content {
@extend .content; @extend .content;
overflow: hidden;
@include border-top-right-radius(4px);
@include border-bottom-right-radius(4px);
h1 { h1 {
@extend .top-header; margin: 0 0 lh();
} }
p { p {
...@@ -159,18 +172,71 @@ div.course-wrapper { ...@@ -159,18 +172,71 @@ div.course-wrapper {
margin-bottom: 15px; margin-bottom: 15px;
padding: 0 0 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 { &:last-child {
border-bottom: none; border-bottom: none;
margin-bottom: 0; margin-bottom: 0;
padding-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 { section.tutorials {
h2 {
margin-bottom: lh();
}
ul { ul {
list-style: disc outside none; margin: 0;
margin-left: lh(); @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 { ...@@ -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 { &.closed {
......
nav.sequence-nav { nav.sequence-nav {
@extend .topbar; @extend .topbar;
@include box-sizing(border-box); border-bottom: 1px solid darken($cream, 20%);
margin-bottom: $body-line-height; margin-bottom: $body-line-height;
position: relative; position: relative;
@include border-top-right-radius(4px);
ol { ol {
border-bottom: 1px solid darken($cream, 20%);
@include box-sizing(border-box); @include box-sizing(border-box);
display: table; display: table;
height: 100%;
padding-right: flex-grid(1, 9); padding-right: flex-grid(1, 9);
width: 100%; width: 100%;
...@@ -61,116 +62,117 @@ nav.sequence-nav { ...@@ -61,116 +62,117 @@ nav.sequence-nav {
display: block; display: block;
height: 17px; height: 17px;
padding: 15px 0 14px; padding: 15px 0 14px;
position: relative;
@include transition(all, .4s, $ease-in-out-quad); @include transition(all, .4s, $ease-in-out-quad);
width: 100%; width: 100%;
// @media screen and (max-width: 800px) { //video
// padding: 12px 8px; &.seq_video_inactive {
// } @extend .inactive;
background-image: url('../images/sequence-nav/video-icon-normal.png');
//video background-position: center;
&.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;
}
&.seq_video_active { &.seq_video_visited {
@extend .active; @extend .visited;
background-image: url('../images/sequence-nav/video-icon-current.png'); background-image: url('../images/sequence-nav/video-icon-visited.png');
background-position: center; background-position: center;
} }
//other &.seq_video_active {
&.seq_other_inactive { @extend .active;
@extend .inactive; background-image: url('../images/sequence-nav/video-icon-current.png');
background-image: url('../images/sequence-nav/document-icon-normal.png'); background-position: center;
background-position: center; }
}
&.seq_other_visited { //other
@extend .visited; &.seq_other_inactive {
background-image: url('../images/sequence-nav/document-icon-visited.png'); @extend .inactive;
background-position: center; background-image: url('../images/sequence-nav/document-icon-normal.png');
} background-position: center;
}
&.seq_other_active { &.seq_other_visited {
@extend .active; @extend .visited;
background-image: url('../images/sequence-nav/document-icon-current.png'); background-image: url('../images/sequence-nav/document-icon-visited.png');
background-position: center; background-position: center;
} }
//vertical & problems &.seq_other_active {
&.seq_vertical_inactive, &.seq_problem_inactive { @extend .active;
@extend .inactive; background-image: url('../images/sequence-nav/document-icon-current.png');
background-image: url('../images/sequence-nav/list-icon-normal.png'); background-position: center;
background-position: center; }
}
&.seq_vertical_visited, &.seq_problem_visited { //vertical & problems
@extend .visited; &.seq_vertical_inactive, &.seq_problem_inactive {
background-image: url('../images/sequence-nav/list-icon-visited.png'); @extend .inactive;
background-position: center; background-image: url('../images/sequence-nav/list-icon-normal.png');
} background-position: center;
}
&.seq_vertical_active, &.seq_problem_active { &.seq_vertical_visited, &.seq_problem_visited {
@extend .active; @extend .visited;
background-image: url('../images/sequence-nav/list-icon-current.png'); background-image: url('../images/sequence-nav/list-icon-visited.png');
background-position: center; background-position: center;
}
} }
p { &.seq_vertical_active, &.seq_problem_active {
// display: none; @extend .active;
// visibility: hidden; background-image: url('../images/sequence-nav/list-icon-current.png');
background: #333; background-position: center;
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;
} }
&:empty { p {
background: none; 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 { &::after {
display: none; background: #333;
content: " ";
display: block;
height: 10px;
left: 18px;
position: absolute;
top: -5px;
@include transform(rotate(45deg));
width: 10px;
} }
} }
&::after { &:hover {
background: #333; p {
content: " "; display: block;
display: block; margin-top: 4px;
height: 10px; opacity: 1;
left: 18px; }
position: absolute;
top: -5px;
@include transform(rotate(45deg));
width: 10px;
} }
} }
} }
} }
ul { ul {
margin-right: 1px; list-style: none !important;
height: 100%;
position: absolute; position: absolute;
right: 0; right: 0;
top: 0; top: 0;
...@@ -220,6 +222,7 @@ nav.sequence-nav { ...@@ -220,6 +222,7 @@ nav.sequence-nav {
&.next { &.next {
a { a {
background-image: url('../images/sequence-nav/next-icon.png'); background-image: url('../images/sequence-nav/next-icon.png');
@include border-top-right-radius(4px);
&:hover { &:hover {
background-color: none; background-color: none;
...@@ -232,26 +235,20 @@ nav.sequence-nav { ...@@ -232,26 +235,20 @@ nav.sequence-nav {
section.course-content { section.course-content {
position: relative;
div#seq_content {
margin-bottom: 60px;
}
nav.sequence-bottom { nav.sequence-bottom {
bottom: (-(lh())); margin: lh(2) 0 0;
position: relative; text-align: center;
ul { ul {
@extend .clearfix; @extend .clearfix;
background-color: darken(#F6EFD4, 5%); background-color: darken(#F6EFD4, 5%);
background-color: darken($cream, 5%); background-color: darken($cream, 5%);
border: 1px solid darken(#f6efd4, 20%); border: 1px solid darken(#f6efd4, 20%);
border-bottom: 0; @include border-radius(3px);
@include border-radius(3px 3px 0 0);
@include box-shadow(inset 0 0 0 1px lighten(#f6efd4, 5%)); @include box-shadow(inset 0 0 0 1px lighten(#f6efd4, 5%));
margin: 0 auto; @include inline-block();
overflow: hidden;
width: 106px;
li { li {
float: left; float: left;
...@@ -264,15 +261,13 @@ section.course-content { ...@@ -264,15 +261,13 @@ section.course-content {
background-repeat: no-repeat; background-repeat: no-repeat;
border-bottom: none; border-bottom: none;
display: block; display: block;
display: block; padding: lh(.5) 4px;
padding: lh(.75) 4px;
text-indent: -9999px; text-indent: -9999px;
@include transition(all, .4s, $ease-in-out-quad); @include transition(all, .4s, $ease-in-out-quad);
width: 45px; width: 45px;
&:hover { &:hover {
background-color: darken($cream, 10%); background-color: darken($cream, 10%);
color: darken(#F6EFD4, 60%);
color: darken($cream, 60%); color: darken($cream, 60%);
opacity: .5; opacity: .5;
text-decoration: none; text-decoration: none;
...@@ -288,6 +283,7 @@ section.course-content { ...@@ -288,6 +283,7 @@ section.course-content {
&.prev { &.prev {
a { a {
background-image: url('../images/sequence-nav/previous-icon.png'); background-image: url('../images/sequence-nav/previous-icon.png');
border-right: 1px solid darken(#f6efd4, 20%);
&:hover { &:hover {
background-color: none; background-color: none;
......
...@@ -26,6 +26,7 @@ section.course-index { ...@@ -26,6 +26,7 @@ section.course-index {
} }
&.ui-state-active { &.ui-state-active {
@include background-image(linear-gradient(-90deg, rgb(245,245,245), rgb(225,225,225)));
@extend .active; @extend .active;
} }
} }
...@@ -33,58 +34,99 @@ section.course-index { ...@@ -33,58 +34,99 @@ section.course-index {
ul.ui-accordion-content { ul.ui-accordion-content {
@include border-radius(0); @include border-radius(0);
@include box-shadow( inset -1px 0 0 #e6e6e6); @include box-shadow(inset -1px 0 0 #e6e6e6);
background: #dadada; background: #dadada;
border: none; border: none;
border-bottom: 1px solid #c3c3c3; border-bottom: 1px solid #c3c3c3;
font-size: 12px; font-size: 12px;
margin: 0; margin: 0;
// overflow: visible; padding: 1em 1.5em;
li { li {
background: transparent;
border: 1px solid transparent;
@include border-radius(4px);
margin-bottom: lh(.5);
position: relative; position: relative;
padding: 5px 36px 5px 10px;
&.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));
// }
}
a { a {
text-decoration: none; text-decoration: none;
margin-bottom: lh(.5);
display: block; display: block;
color: #000; color: #666;
&:hover {
color: #666;
}
p { p {
font-weight: bold;
margin-bottom: 0; margin-bottom: 0;
&.subtitle { span.subtitle {
color: #666; 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 { ...@@ -42,16 +42,6 @@ div.answer-block {
padding-top: 20px; padding-top: 20px;
width: 100%; 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 { img.answer-img-accept {
margin: 10px 0px 10px 16px; margin: 10px 0px 10px 16px;
} }
......
div.question-header { 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 { div.vote-buttons {
display: inline-block; display: inline-block;
float: left; float: left;
......
li.calc-main { li.calc-main {
bottom: -36px; bottom: -126px;
left: 0; left: 0;
position: fixed; position: fixed;
@include transition(bottom);
-webkit-appearance: none;
width: 100%; width: 100%;
z-index: 99;
&.open {
bottom: -36px;
div#calculator_wrapper form div.input-wrapper div.help-wrapper dl {
display: block;
}
}
a.calc { a.calc {
@include hide-text;
background: url("../images/calc-icon.png") rgba(#111, .9) no-repeat center; background: url("../images/calc-icon.png") rgba(#111, .9) no-repeat center;
border-bottom: 0; border-bottom: 0;
@include border-radius(3px 3px 0 0);
color: #fff; color: #fff;
float: right; float: right;
margin-right: 10px; height: 20px;
@include border-radius(3px 3px 0 0); @include hide-text;
@include inline-block; @include inline-block;
margin-right: 10px;
padding: 8px 12px; padding: 8px 12px;
width: 16px;
height: 20px;
position: relative; position: relative;
top: -36px; top: -36px;
width: 16px;
&:hover { &:hover {
opacity: .8; opacity: .8;
...@@ -30,14 +41,15 @@ li.calc-main { ...@@ -30,14 +41,15 @@ li.calc-main {
div#calculator_wrapper { div#calculator_wrapper {
background: rgba(#111, .9); background: rgba(#111, .9);
clear: both;
max-height: 90px;
position: relative; position: relative;
top: -36px; top: -36px;
clear: both;
form { form {
padding: lh();
@extend .clearfix; @extend .clearfix;
@include box-sizing(border-box);
padding: lh();
input#calculator_button { input#calculator_button {
background: #111; background: #111;
...@@ -46,13 +58,14 @@ li.calc-main { ...@@ -46,13 +58,14 @@ li.calc-main {
@include box-shadow(none); @include box-shadow(none);
@include box-sizing(border-box); @include box-sizing(border-box);
color: #fff; color: #fff;
float: left;
font-size: 30px; font-size: 30px;
font-weight: bold; font-weight: bold;
margin: 0 (flex-gutter() / 2);
padding: 0; padding: 0;
text-shadow: none; text-shadow: none;
-webkit-appearance: none;
width: flex-grid(.5) + flex-gutter(); width: flex-grid(.5) + flex-gutter();
float: left;
margin: 0 (flex-gutter() / 2);
&:hover { &:hover {
color: #333; color: #333;
...@@ -70,29 +83,31 @@ li.calc-main { ...@@ -70,29 +83,31 @@ li.calc-main {
font-weight: bold; font-weight: bold;
margin: 1px 0 0; margin: 1px 0 0;
padding: 10px; padding: 10px;
-webkit-appearance: none;
width: flex-grid(4); width: flex-grid(4);
} }
div.input-wrapper { div.input-wrapper {
position: relative;
@extend .clearfix; @extend .clearfix;
width: flex-grid(7.5);
margin: 0;
float: left; float: left;
margin: 0;
position: relative;
width: flex-grid(7.5);
input#calculator_input { input#calculator_input {
border: none;
@include box-shadow(none);
@include box-sizing(border-box);
font-size: 16px;
padding: 10px;
width: 100%;
&:focus {
outline: none;
border: none; 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 { div.help-wrapper {
position: absolute; position: absolute;
...@@ -100,10 +115,10 @@ li.calc-main { ...@@ -100,10 +115,10 @@ li.calc-main {
top: 15px; top: 15px;
a { a {
background: url("../images/info-icon.png") center center no-repeat;
height: 17px;
@include hide-text; @include hide-text;
width: 17px; width: 17px;
height: 17px;
background: url("../images/info-icon.png") center center no-repeat;
} }
dl { dl {
...@@ -111,13 +126,14 @@ li.calc-main { ...@@ -111,13 +126,14 @@ li.calc-main {
@include border-radius(3px); @include border-radius(3px);
@include box-shadow(0 0 3px #999); @include box-shadow(0 0 3px #999);
color: #333; color: #333;
display: none;
opacity: 0; opacity: 0;
padding: 10px; padding: 10px;
position: absolute; position: absolute;
right: -40px; right: -40px;
top: -110px; top: -110px;
width: 500px;
@include transition(); @include transition();
width: 500px;
&.shown { &.shown {
opacity: 1; opacity: 1;
......
...@@ -68,11 +68,11 @@ footer { ...@@ -68,11 +68,11 @@ footer {
} }
a { a {
border-bottom: 0;
display: block; display: block;
height: 29px; height: 29px;
width: 28px;
text-indent: -9999px; text-indent: -9999px;
border-bottom: 0; width: 28px;
&:hover { &:hover {
opacity: .8; opacity: .8;
......
...@@ -100,12 +100,12 @@ div.header-wrapper { ...@@ -100,12 +100,12 @@ div.header-wrapper {
float: left; float: left;
a { a {
border: none;
color: #fff; color: #fff;
display: block; display: block;
font-style: normal;
font-weight: bold; font-weight: bold;
padding: 10px lh() 8px; padding: 10px lh() 8px;
border: none;
font-style: normal;
@media screen and (max-width: 1020px) { @media screen and (max-width: 1020px) {
padding: 10px lh(.7) 8px; padding: 10px lh(.7) 8px;
...@@ -125,10 +125,10 @@ div.header-wrapper { ...@@ -125,10 +125,10 @@ div.header-wrapper {
ul { ul {
li { li {
padding: auto;
display: table-cell; display: table-cell;
width: 16.6666666667%; padding: auto;
text-align: center; text-align: center;
width: 16.6666666667%;
} }
} }
} }
......
...@@ -2,11 +2,11 @@ html { ...@@ -2,11 +2,11 @@ html {
margin-top: 0; margin-top: 0;
body { body {
background: #f4f4f4; //#f3f1e5
color: $dark-gray; color: $dark-gray;
font: $body-font-size $body-font-family; font: $body-font-size $body-font-family;
text-align: center;
margin: 0; margin: 0;
background: #f4f4f4; //#f3f1e5 text-align: center;
section.main-content { section.main-content {
@extend .clearfix; @extend .clearfix;
...@@ -17,7 +17,7 @@ html { ...@@ -17,7 +17,7 @@ html {
@include box-shadow(0 0 4px #dfdfdf); @include box-shadow(0 0 4px #dfdfdf);
@include box-sizing(border-box); @include box-sizing(border-box);
margin-top: 3px; margin-top: 3px;
// overflow: hidden; overflow: hidden;
@media print { @media print {
border-bottom: 0; border-bottom: 0;
...@@ -30,6 +30,18 @@ html { ...@@ -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 { section.outside-app {
@extend .main-content; @extend .main-content;
max-width: 600px; max-width: 600px;
......
#lean_overlay { #lean_overlay {
background: #000;
display: none;
height:100%;
left: 0px;
position: fixed; position: fixed;
z-index:100;
top: 0px; top: 0px;
left: 0px;
height:100%;
width:100%; width:100%;
background: #000; z-index:100;
display: none;
} }
div.leanModal_box { div.leanModal_box {
...@@ -31,8 +31,8 @@ div.leanModal_box { ...@@ -31,8 +31,8 @@ div.leanModal_box {
z-index: 2; z-index: 2;
&:hover{ &:hover{
text-decoration: none;
color: $mit-red; color: $mit-red;
text-decoration: none;
} }
} }
...@@ -55,8 +55,8 @@ div.leanModal_box { ...@@ -55,8 +55,8 @@ div.leanModal_box {
li { li {
&.terms, &.honor-code { &.terms, &.honor-code {
width: auto;
float: none; float: none;
width: auto;
} }
div.tip { div.tip {
...@@ -118,8 +118,8 @@ div.leanModal_box { ...@@ -118,8 +118,8 @@ div.leanModal_box {
} }
&.honor-code { &.honor-code {
width: auto;
float: none; float: none;
width: auto;
} }
label { label {
...@@ -128,8 +128,8 @@ div.leanModal_box { ...@@ -128,8 +128,8 @@ div.leanModal_box {
} }
#{$all-text-inputs}, textarea { #{$all-text-inputs}, textarea {
width: 100%;
@include box-sizing(border-box); @include box-sizing(border-box);
width: 100%;
} }
input[type="checkbox"] { input[type="checkbox"] {
...@@ -176,15 +176,15 @@ div#login { ...@@ -176,15 +176,15 @@ div#login {
ol { ol {
li { li {
width: auto;
float: none; float: none;
width: auto;
} }
} }
} }
div.lost-password { div.lost-password {
text-align: left;
margin-top: lh(); margin-top: lh();
text-align: left;
a { a {
color: #999; color: #999;
...@@ -218,9 +218,9 @@ div#deactivate-account { ...@@ -218,9 +218,9 @@ div#deactivate-account {
margin-bottom: lh(.5); margin-bottom: lh(.5);
textarea, #{$all-text-inputs} { textarea, #{$all-text-inputs} {
@include box-sizing(border-box);
display: block; display: block;
width: 100%; width: 100%;
@include box-sizing(border-box);
} }
textarea { textarea {
......
...@@ -2,5 +2,5 @@ ...@@ -2,5 +2,5 @@
@import "base/reset", "base/font-face", "base/functions"; @import "base/reset", "base/font-face", "base/functions";
// pages // 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"; @import "layout/leanmodal";
...@@ -6,7 +6,7 @@ footer { ...@@ -6,7 +6,7 @@ footer {
div.footer-wrapper { div.footer-wrapper {
border-top: 1px solid #e5e5e5; border-top: 1px solid #e5e5e5;
padding: lh() 0; 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) { @media screen and (max-width: 780px) {
background-position: left bottom; background-position: left bottom;
...@@ -84,15 +84,15 @@ footer { ...@@ -84,15 +84,15 @@ footer {
} }
&.twitter a { &.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 { &.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 { &.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 { ...@@ -6,10 +6,10 @@ header.announcement {
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
&.home { &.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) { @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 { div {
...@@ -33,14 +33,14 @@ header.announcement { ...@@ -33,14 +33,14 @@ header.announcement {
} }
&.course { &.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) { @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) { @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 { div {
......
...@@ -20,6 +20,7 @@ section.index-content { ...@@ -20,6 +20,7 @@ section.index-content {
p { p {
line-height: lh(); line-height: lh();
margin-bottom: lh(); margin-bottom: lh();
} }
ul { ul {
...@@ -221,22 +222,35 @@ section.index-content { ...@@ -221,22 +222,35 @@ section.index-content {
&.course { &.course {
h2 { h2 {
padding-top: lh(5); 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); @include background-size(contain);
@media screen and (max-width: 998px) and (min-width: 781px){ @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) { @media screen and (max-width: 780px) {
padding-top: lh(5); 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) { @media screen and (min-width: 500px) and (max-width: 781px) {
padding-top: lh(8); padding-top: lh(8);
} }
} }
div.announcement {
p.announcement-button {
a {
margin-top: 0;
}
}
img {
max-width: 100%;
margin-bottom: lh();
}
}
} }
......
form#wiki_revision { form#wiki_revision {
float: left; float: left;
width: flex-grid(6, 9);
margin-right: flex-gutter(9); margin-right: flex-gutter(9);
width: flex-grid(6, 9);
label { label {
display: block; display: block;
margin-bottom: 7px ; margin-bottom: 7px ;
} }
.CodeMirror-scroll { .CodeMirror-scroll {
min-height: 550px; min-height: 550px;
width: 100%; width: 100%;
} }
.CodeMirror { .CodeMirror {
@extend textarea; @extend textarea;
@include box-sizing(border-box); @include box-sizing(border-box);
font-family: monospace; font-family: monospace;
margin-bottom: 20px; margin-bottom: 20px;
} }
textarea { textarea {
@include box-sizing(border-box); @include box-sizing(border-box);
margin-bottom: 20px; margin-bottom: 20px;
...@@ -32,25 +33,25 @@ form#wiki_revision { ...@@ -32,25 +33,25 @@ form#wiki_revision {
} }
#submit_delete { #submit_delete {
@include box-shadow(none);
background: none; background: none;
border: none; border: none;
@include box-shadow(none);
color: #999; color: #999;
float: right; float: right;
text-decoration: underline;
font-weight: normal; font-weight: normal;
text-decoration: underline;
} }
input[type="submit"] { input[type="submit"] {
margin-top: 20px; margin-top: 20px;
} }
} }
#wiki_edit_instructions { #wiki_edit_instructions {
color: #666;
float: left; float: left;
width: flex-grid(3, 9);
margin-top: lh(); margin-top: lh();
color: #666; width: flex-grid(3, 9);
&:hover { &:hover {
color: #333; color: #333;
...@@ -58,16 +59,14 @@ form#wiki_revision { ...@@ -58,16 +59,14 @@ form#wiki_revision {
.markdown-example { .markdown-example {
background-color: #e3e3e3; background-color: #e3e3e3;
text-shadow: 0 1px 0 #fff; line-height: 1.0;
margin: 5px 0 7px;
padding: { padding: {
top: 5px; top: 5px;
right: 2px; right: 2px;
bottom: 5px; bottom: 5px;
left: 5px; left: 5px;
} }
text-shadow: 0 1px 0 #fff;
margin: 5px 0 7px;
line-height: 1.0;
} }
} }
...@@ -3,20 +3,20 @@ div#wiki_panel { ...@@ -3,20 +3,20 @@ div#wiki_panel {
overflow: auto; overflow: auto;
h2 { h2 {
padding: lh(.5) lh(); @extend .bottom-border;
font-size: 18px; font-size: 18px;
margin: 0 ; margin: 0 ;
@extend .bottom-border; padding: lh(.5) lh();
} }
input[type="button"] { input[type="button"] {
@extend h3; @extend h3;
@include transition();
color: lighten($text-color, 10%); color: lighten($text-color, 10%);
font-size: $body-font-size; font-size: $body-font-size;
margin: 0 !important; margin: 0 !important;
padding: 7px lh(); padding: 7px lh();
text-align: left; text-align: left;
@include transition();
width: 100%; width: 100%;
&:hover { &:hover {
...@@ -28,8 +28,8 @@ div#wiki_panel { ...@@ -28,8 +28,8 @@ div#wiki_panel {
ul { ul {
li { li {
&.search { &.search {
@include box-shadow(0 1px 0 #eee);
border-bottom: 1px solid #d3d3d3; border-bottom: 1px solid #d3d3d3;
@include box-shadow(0 1px 0 #eee);
padding: 7px lh(); padding: 7px lh();
label { label {
...@@ -49,15 +49,15 @@ div#wiki_panel { ...@@ -49,15 +49,15 @@ div#wiki_panel {
div#wiki_create_form { div#wiki_create_form {
@extend .clearfix; @extend .clearfix;
padding: 15px;
background: #d6d6d6; background: #d6d6d6;
border-bottom: 1px solid #bbb; border-bottom: 1px solid #bbb;
padding: 15px;
input[type="text"] { input[type="text"] {
margin-bottom: 6px; @include box-sizing(border-box);
display: block; display: block;
margin-bottom: 6px;
width: 100%; width: 100%;
@include box-sizing(border-box);
} }
ul { ul {
......
...@@ -7,15 +7,14 @@ div.wiki-wrapper { ...@@ -7,15 +7,14 @@ div.wiki-wrapper {
@extend .content; @extend .content;
position: relative; position: relative;
header { header {
@extend .topbar; @extend .topbar;
height:46px;
@include box-shadow(inset 0 1px 0 white); @include box-shadow(inset 0 1px 0 white);
height:46px;
&:empty { &:empty {
display: none !important;
border-bottom: 0; border-bottom: 0;
display: none !important;
} }
a { a {
...@@ -23,10 +22,10 @@ div.wiki-wrapper { ...@@ -23,10 +22,10 @@ div.wiki-wrapper {
} }
p { p {
float: left;
margin-bottom: 0;
color: darken($cream, 55%); color: darken($cream, 55%);
float: left;
line-height: 46px; line-height: 46px;
margin-bottom: 0;
padding-left: lh(); padding-left: lh();
} }
...@@ -48,8 +47,8 @@ div.wiki-wrapper { ...@@ -48,8 +47,8 @@ div.wiki-wrapper {
@include box-shadow(inset 1px 0 0 lighten(#f6efd4, 5%)); @include box-shadow(inset 1px 0 0 lighten(#f6efd4, 5%));
color: darken($cream, 80%); color: darken($cream, 80%);
display: block; display: block;
font-weight: normal;
font-size: 12px; font-size: 12px;
font-weight: normal;
letter-spacing: 1px; letter-spacing: 1px;
line-height: 46px; line-height: 46px;
margin: 0; margin: 0;
...@@ -89,15 +88,15 @@ div.wiki-wrapper { ...@@ -89,15 +88,15 @@ div.wiki-wrapper {
width: flex-grid(2.5, 9); width: flex-grid(2.5, 9);
@media screen and (max-width:900px) { @media screen and (max-width:900px) {
border-right: 0;
display: block; display: block;
width: auto; width: auto;
border-right: 0;
} }
@media print { @media print {
border-right: 0;
display: block; display: block;
width: auto; width: auto;
border-right: 0;
} }
} }
...@@ -106,9 +105,9 @@ div.wiki-wrapper { ...@@ -106,9 +105,9 @@ div.wiki-wrapper {
} }
section.results { section.results {
border-left: 1px dashed #ddd;
@include box-sizing(border-box); @include box-sizing(border-box);
display: inline-block; display: inline-block;
border-left: 1px dashed #ddd;
float: left; float: left;
padding-left: 10px; padding-left: 10px;
width: flex-grid(6.5, 9); width: flex-grid(6.5, 9);
...@@ -123,8 +122,8 @@ div.wiki-wrapper { ...@@ -123,8 +122,8 @@ div.wiki-wrapper {
@media print { @media print {
display: block; display: block;
width: auto;
padding: 0; padding: 0;
width: auto;
canvas, img { canvas, img {
page-break-inside: avoid; page-break-inside: avoid;
...@@ -140,14 +139,15 @@ div.wiki-wrapper { ...@@ -140,14 +139,15 @@ div.wiki-wrapper {
} }
li { li {
border-bottom: 1px solid #eee;
list-style: none; list-style: none;
margin: 0; margin: 0;
padding: 10px 0; padding: 10px 0;
border-bottom: 1px solid #eee;
&:last-child { &:last-child {
border-bottom: 0; border-bottom: 0;
} }
h3 { h3 {
font-size: 18px; font-size: 18px;
font-weight: normal; font-weight: normal;
...@@ -155,6 +155,5 @@ div.wiki-wrapper { ...@@ -155,6 +155,5 @@ div.wiki-wrapper {
} }
} }
} }
} }
} }
...@@ -5,6 +5,30 @@ import tempfile ...@@ -5,6 +5,30 @@ import tempfile
import djcelery 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 # from settings2.askbotsettings import LIVESETTINGS_OPTIONS
DEFAULT_GROUPS = [] DEFAULT_GROUPS = []
...@@ -28,7 +52,6 @@ sys.path.append(BASE_DIR + "/mitx/lib") ...@@ -28,7 +52,6 @@ sys.path.append(BASE_DIR + "/mitx/lib")
COURSEWARE_ENABLED = True COURSEWARE_ENABLED = True
ASKBOT_ENABLED = True ASKBOT_ENABLED = True
CSRF_COOKIE_DOMAIN = '127.0.0.1'
# Defaults to be overridden # Defaults to be overridden
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
...@@ -39,8 +62,8 @@ DEFAULT_FEEDBACK_EMAIL = 'feedback@mitx.mit.edu' ...@@ -39,8 +62,8 @@ DEFAULT_FEEDBACK_EMAIL = 'feedback@mitx.mit.edu'
GENERATE_RANDOM_USER_CREDENTIALS = False GENERATE_RANDOM_USER_CREDENTIALS = False
WIKI_REQUIRE_LOGIN_EDIT = True SIMPLE_WIKI_REQUIRE_LOGIN_EDIT = True
WIKI_REQUIRE_LOGIN_VIEW = True SIMPLE_WIKI_REQUIRE_LOGIN_VIEW = False
PERFSTATS = False PERFSTATS = False
...@@ -116,9 +139,11 @@ MIDDLEWARE_CLASSES = ( ...@@ -116,9 +139,11 @@ MIDDLEWARE_CLASSES = (
'django.middleware.csrf.CsrfViewMiddleware', 'django.middleware.csrf.CsrfViewMiddleware',
#'django.contrib.auth.middleware.AuthenticationMiddleware', #'django.contrib.auth.middleware.AuthenticationMiddleware',
'cache_toolbox.middleware.CacheBackedAuthenticationMiddleware', 'cache_toolbox.middleware.CacheBackedAuthenticationMiddleware',
'masquerade.middleware.MasqueradeMiddleware',
'django.contrib.messages.middleware.MessageMiddleware', 'django.contrib.messages.middleware.MessageMiddleware',
'track.middleware.TrackMiddleware', 'track.middleware.TrackMiddleware',
'mitxmako.middleware.MakoMiddleware', 'mitxmako.middleware.MakoMiddleware',
#'ssl_auth.ssl_auth.NginxProxyHeaderMiddleware', # ssl authentication behind nginx proxy
#'debug_toolbar.middleware.DebugToolbarMiddleware', #'debug_toolbar.middleware.DebugToolbarMiddleware',
# Uncommenting the following will prevent csrf token from being re-set if you # Uncommenting the following will prevent csrf token from being re-set if you
...@@ -146,6 +171,10 @@ INSTALLED_APPS = ( ...@@ -146,6 +171,10 @@ INSTALLED_APPS = (
'circuit', 'circuit',
'perfstats', 'perfstats',
'util', 'util',
'masquerade',
'django_jasmine',
#'ssl_auth', ## Broken. Disabled for now.
'multicourse', # multiple courses
# Uncomment the next line to enable the admin: # Uncomment the next line to enable the admin:
# 'django.contrib.admin', # 'django.contrib.admin',
# Uncomment the next line to enable admin documentation: # Uncomment the next line to enable admin documentation:
...@@ -346,6 +375,7 @@ PROJECT_ROOT = os.path.dirname(__file__) ...@@ -346,6 +375,7 @@ PROJECT_ROOT = os.path.dirname(__file__)
TEMPLATE_CONTEXT_PROCESSORS = ( TEMPLATE_CONTEXT_PROCESSORS = (
'django.core.context_processors.request', 'django.core.context_processors.request',
'django.core.context_processors.static',
'askbot.context.application_settings', 'askbot.context.application_settings',
#'django.core.context_processors.i18n', #'django.core.context_processors.i18n',
'askbot.user_messages.context_processors.user_messages',#must be before auth 'askbot.user_messages.context_processors.user_messages',#must be before auth
...@@ -369,8 +399,8 @@ INSTALLED_APPS = INSTALLED_APPS + ( ...@@ -369,8 +399,8 @@ INSTALLED_APPS = INSTALLED_APPS + (
CACHE_MIDDLEWARE_ANONYMOUS_ONLY = True CACHE_MIDDLEWARE_ANONYMOUS_ONLY = True
ASKBOT_URL = 'discussion/' ASKBOT_URL = 'discussion/'
LOGIN_REDIRECT_URL = '/' LOGIN_REDIRECT_URL = MITX_ROOT_URL + '/'
LOGIN_URL = '/' LOGIN_URL = MITX_ROOT_URL + '/'
# ASKBOT_UPLOADED_FILES_URL = '%s%s' % (ASKBOT_URL, 'upfiles/') # ASKBOT_UPLOADED_FILES_URL = '%s%s' % (ASKBOT_URL, 'upfiles/')
ALLOW_UNICODE_SLUGS = False ALLOW_UNICODE_SLUGS = False
...@@ -500,10 +530,11 @@ LIVESETTINGS_OPTIONS = { ...@@ -500,10 +530,11 @@ LIVESETTINGS_OPTIONS = {
'CUSTOM_HEADER' : u'', 'CUSTOM_HEADER' : u'',
'CUSTOM_HTML_HEAD' : u'', 'CUSTOM_HTML_HEAD' : u'',
'CUSTOM_JS' : u'', 'CUSTOM_JS' : u'',
'SITE_FAVICON' : u'/images/favicon.gif', 'MITX_ROOT_URL' : MITX_ROOT_URL, # for askbot header.html file
'SITE_LOGO_URL' : u'/images/logo.gif', 'SITE_FAVICON' : unicode(MITX_ROOT_URL) + u'/images/favicon.gif',
'SITE_LOGO_URL' :unicode(MITX_ROOT_URL) + u'/images/logo.gif',
'SHOW_LOGO' : False, '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, 'ALWAYS_SHOW_ALL_UI_FUNCTIONS' : False,
'ASKBOT_DEFAULT_SKIN' : u'default', 'ASKBOT_DEFAULT_SKIN' : u'default',
'USE_CUSTOM_HTML_HEAD' : False, 'USE_CUSTOM_HTML_HEAD' : False,
...@@ -536,12 +567,12 @@ LIVESETTINGS_OPTIONS = { ...@@ -536,12 +567,12 @@ LIVESETTINGS_OPTIONS = {
'SIGNIN_WORDPRESS_ENABLED' : True, 'SIGNIN_WORDPRESS_ENABLED' : True,
'SIGNIN_WORDPRESS_SITE_ENABLED' : False, 'SIGNIN_WORDPRESS_SITE_ENABLED' : False,
'SIGNIN_YAHOO_ENABLED' : True, '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' : '', 'WORDPRESS_SITE_URL' : '',
}, },
'LICENSE_SETTINGS' : { 'LICENSE_SETTINGS' : {
'LICENSE_ACRONYM' : u'cc-by-sa', '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_TITLE' : u'Creative Commons Attribution Share Alike 3.0',
'LICENSE_URL' : 'http://creativecommons.org/licenses/by-sa/3.0/legalcode', 'LICENSE_URL' : 'http://creativecommons.org/licenses/by-sa/3.0/legalcode',
'LICENSE_USE_LOGO' : True, 'LICENSE_USE_LOGO' : True,
...@@ -682,3 +713,5 @@ if MAKO_MODULE_DIR == None: ...@@ -682,3 +713,5 @@ if MAKO_MODULE_DIR == None:
djcelery.setup_loader() 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