Commit 58b81472 by Calen Pennington

Cleaning up pep8 violations

parent 9f237d2e
...@@ -19,10 +19,12 @@ def user(email): ...@@ -19,10 +19,12 @@ def user(email):
'''look up a user by email''' '''look up a user by email'''
return User.objects.get(email=email) return User.objects.get(email=email)
def registration(email): def registration(email):
'''look up registration object by email''' '''look up registration object by email'''
return Registration.objects.get(user__email=email) return Registration.objects.get(user__email=email)
class AuthTestCase(TestCase): class AuthTestCase(TestCase):
"""Check that various permissions-related things work""" """Check that various permissions-related things work"""
...@@ -60,11 +62,11 @@ class AuthTestCase(TestCase): ...@@ -60,11 +62,11 @@ class AuthTestCase(TestCase):
'username': username, 'username': username,
'email': email, 'email': email,
'password': pw, 'password': pw,
'location' : 'home', 'location': 'home',
'language' : 'Franglish', 'language': 'Franglish',
'name' : 'Fred Weasley', 'name': 'Fred Weasley',
'terms_of_service' : 'true', 'terms_of_service': 'true',
'honor_code' : 'true', 'honor_code': 'true',
}) })
return resp return resp
...@@ -99,7 +101,6 @@ class AuthTestCase(TestCase): ...@@ -99,7 +101,6 @@ class AuthTestCase(TestCase):
self.create_account(self.username, self.email, self.pw) self.create_account(self.username, self.email, self.pw)
self.activate_user(self.email) self.activate_user(self.email)
def _login(self, email, pw): def _login(self, email, pw):
'''Login. View should always return 200. The success/fail is in the '''Login. View should always return 200. The success/fail is in the
returned json''' returned json'''
...@@ -108,7 +109,6 @@ class AuthTestCase(TestCase): ...@@ -108,7 +109,6 @@ class AuthTestCase(TestCase):
self.assertEqual(resp.status_code, 200) self.assertEqual(resp.status_code, 200)
return resp return resp
def login(self, email, pw): def login(self, email, pw):
'''Login, check that it worked.''' '''Login, check that it worked.'''
resp = self._login(self.email, self.pw) resp = self._login(self.email, self.pw)
...@@ -163,7 +163,6 @@ class AuthTestCase(TestCase): ...@@ -163,7 +163,6 @@ class AuthTestCase(TestCase):
print "Checking '{0}'".format(page) print "Checking '{0}'".format(page)
self.check_page_get(page, expected=200) self.check_page_get(page, expected=200)
def test_index_auth(self): def test_index_auth(self):
# not logged in. Should return a redirect. # not logged in. Should return a redirect.
......
...@@ -50,4 +50,3 @@ class PostReceiveTestCase(TestCase): ...@@ -50,4 +50,3 @@ class PostReceiveTestCase(TestCase):
import_from_github.assert_called_with(settings.REPOS['repo']) import_from_github.assert_called_with(settings.REPOS['repo'])
mock_revision, mock_course = import_from_github.return_value mock_revision, mock_course = import_from_github.return_value
export_to_github.assert_called_with(mock_course, 'path', "Changes from cms import of revision %s" % mock_revision) export_to_github.assert_called_with(mock_course, 'path', "Changes from cms import of revision %s" % mock_revision)
...@@ -13,6 +13,7 @@ from django.db import DEFAULT_DB_ALIAS ...@@ -13,6 +13,7 @@ from django.db import DEFAULT_DB_ALIAS
from . import app_settings from . import app_settings
def get_instance(model, instance_or_pk, timeout=None, using=None): def get_instance(model, instance_or_pk, timeout=None, using=None):
""" """
Returns the ``model`` instance with a primary key of ``instance_or_pk``. Returns the ``model`` instance with a primary key of ``instance_or_pk``.
...@@ -87,6 +88,7 @@ def get_instance(model, instance_or_pk, timeout=None, using=None): ...@@ -87,6 +88,7 @@ def get_instance(model, instance_or_pk, timeout=None, using=None):
return instance return instance
def delete_instance(model, *instance_or_pk): def delete_instance(model, *instance_or_pk):
""" """
Purges the cache keys for the instances of this model. Purges the cache keys for the instances of this model.
...@@ -94,6 +96,7 @@ def delete_instance(model, *instance_or_pk): ...@@ -94,6 +96,7 @@ def delete_instance(model, *instance_or_pk):
cache.delete_many([instance_key(model, x) for x in instance_or_pk]) cache.delete_many([instance_key(model, x) for x in instance_or_pk])
def instance_key(model, instance_or_pk): def instance_key(model, instance_or_pk):
""" """
Returns the cache key for this (model, instance) pair. Returns the cache key for this (model, instance) pair.
......
...@@ -84,6 +84,7 @@ from django.contrib.auth.middleware import AuthenticationMiddleware ...@@ -84,6 +84,7 @@ from django.contrib.auth.middleware import AuthenticationMiddleware
from .model import cache_model from .model import cache_model
class CacheBackedAuthenticationMiddleware(AuthenticationMiddleware): class CacheBackedAuthenticationMiddleware(AuthenticationMiddleware):
def __init__(self): def __init__(self):
cache_model(User) cache_model(User)
......
...@@ -58,6 +58,7 @@ from django.db.models.signals import post_save, post_delete ...@@ -58,6 +58,7 @@ from django.db.models.signals import post_save, post_delete
from .core import get_instance, delete_instance from .core import get_instance, delete_instance
def cache_model(model, timeout=None): def cache_model(model, timeout=None):
if hasattr(model, 'get_cached'): if hasattr(model, 'get_cached'):
# Already patched # Already patched
......
...@@ -74,6 +74,7 @@ from django.db.models.signals import post_save, post_delete ...@@ -74,6 +74,7 @@ from django.db.models.signals import post_save, post_delete
from .core import get_instance, delete_instance from .core import get_instance, delete_instance
def cache_relation(descriptor, timeout=None): def cache_relation(descriptor, timeout=None):
rel = descriptor.related rel = descriptor.related
related_name = '%s_cache' % rel.field.related_query_name() related_name = '%s_cache' % rel.field.related_query_name()
......
...@@ -5,6 +5,7 @@ from django.template import resolve_variable ...@@ -5,6 +5,7 @@ from django.template import resolve_variable
register = template.Library() register = template.Library()
class CacheNode(Node): class CacheNode(Node):
def __init__(self, nodelist, expire_time, key): def __init__(self, nodelist, expire_time, key):
self.nodelist = nodelist self.nodelist = nodelist
...@@ -21,6 +22,7 @@ class CacheNode(Node): ...@@ -21,6 +22,7 @@ class CacheNode(Node):
cache.set(key, value, expire_time) cache.set(key, value, expire_time)
return value return value
@register.tag @register.tag
def cachedeterministic(parser, token): def cachedeterministic(parser, token):
""" """
...@@ -42,6 +44,7 @@ def cachedeterministic(parser, token): ...@@ -42,6 +44,7 @@ def cachedeterministic(parser, token):
raise TemplateSyntaxError(u"'%r' tag requires 2 arguments." % tokens[0]) raise TemplateSyntaxError(u"'%r' tag requires 2 arguments." % tokens[0])
return CacheNode(nodelist, tokens[1], tokens[2]) return CacheNode(nodelist, tokens[1], tokens[2])
class ShowIfCachedNode(Node): class ShowIfCachedNode(Node):
def __init__(self, key): def __init__(self, key):
self.key = key self.key = key
...@@ -50,6 +53,7 @@ class ShowIfCachedNode(Node): ...@@ -50,6 +53,7 @@ class ShowIfCachedNode(Node):
key = resolve_variable(self.key, context) key = resolve_variable(self.key, context)
return cache.get(key) or '' return cache.get(key) or ''
@register.tag @register.tag
def showifcached(parser, token): def showifcached(parser, token):
""" """
......
...@@ -60,6 +60,7 @@ def csrf_response_exempt(view_func): ...@@ -60,6 +60,7 @@ def csrf_response_exempt(view_func):
PendingDeprecationWarning) PendingDeprecationWarning)
return view_func return view_func
def csrf_view_exempt(view_func): def csrf_view_exempt(view_func):
""" """
Marks a view function as being exempt from CSRF view protection. Marks a view function as being exempt from CSRF view protection.
...@@ -68,6 +69,7 @@ def csrf_view_exempt(view_func): ...@@ -68,6 +69,7 @@ def csrf_view_exempt(view_func):
PendingDeprecationWarning) PendingDeprecationWarning)
return csrf_exempt(view_func) return csrf_exempt(view_func)
def csrf_exempt(view_func): def csrf_exempt(view_func):
""" """
Marks a view function as being exempt from the CSRF view protection. Marks a view function as being exempt from the CSRF view protection.
......
...@@ -6,6 +6,7 @@ from pipeline.conf import settings ...@@ -6,6 +6,7 @@ from pipeline.conf import settings
from pipeline.packager import Packager from pipeline.packager import Packager
from pipeline.utils import guess_type from pipeline.utils import guess_type
def compressed_css(package_name): def compressed_css(package_name):
package = settings.PIPELINE_CSS.get(package_name, {}) package = settings.PIPELINE_CSS.get(package_name, {})
if package: if package:
...@@ -20,6 +21,7 @@ def compressed_css(package_name): ...@@ -20,6 +21,7 @@ def compressed_css(package_name):
paths = packager.compile(package.paths) paths = packager.compile(package.paths)
return render_individual_css(package, paths) return render_individual_css(package, paths)
def render_css(package, path): def render_css(package, path):
template_name = package.template_name or "mako/css.html" template_name = package.template_name or "mako/css.html"
context = package.extra_context context = package.extra_context
...@@ -29,6 +31,7 @@ def render_css(package, path): ...@@ -29,6 +31,7 @@ def render_css(package, path):
}) })
return render_to_string(template_name, context) return render_to_string(template_name, context)
def render_individual_css(package, paths): def render_individual_css(package, paths):
tags = [render_css(package, path) for path in paths] tags = [render_css(package, path) for path in paths]
return '\n'.join(tags) return '\n'.join(tags)
...@@ -49,6 +52,7 @@ def compressed_js(package_name): ...@@ -49,6 +52,7 @@ def compressed_js(package_name):
templates = packager.pack_templates(package) templates = packager.pack_templates(package)
return render_individual_js(package, paths, templates) return render_individual_js(package, paths, templates)
def render_js(package, path): def render_js(package, path):
template_name = package.template_name or "mako/js.html" template_name = package.template_name or "mako/js.html"
context = package.extra_context context = package.extra_context
...@@ -58,6 +62,7 @@ def render_js(package, path): ...@@ -58,6 +62,7 @@ def render_js(package, path):
}) })
return render_to_string(template_name, context) return render_to_string(template_name, context)
def render_inline_js(package, js): def render_inline_js(package, js):
context = package.extra_context context = package.extra_context
context.update({ context.update({
...@@ -65,6 +70,7 @@ def render_inline_js(package, js): ...@@ -65,6 +70,7 @@ def render_inline_js(package, js):
}) })
return render_to_string("mako/inline_js.html", context) return render_to_string("mako/inline_js.html", context)
def render_individual_js(package, paths, templates=None): def render_individual_js(package, paths, templates=None):
tags = [render_js(package, js) for js in paths] tags = [render_js(package, js) for js in paths]
if templates: if templates:
......
...@@ -15,4 +15,3 @@ admin.site.register(CourseEnrollment) ...@@ -15,4 +15,3 @@ admin.site.register(CourseEnrollment)
admin.site.register(Registration) admin.site.register(Registration)
admin.site.register(PendingNameChange) admin.site.register(PendingNameChange)
...@@ -25,6 +25,7 @@ import mitxmako.middleware as middleware ...@@ -25,6 +25,7 @@ import mitxmako.middleware as middleware
middleware.MakoMiddleware() middleware.MakoMiddleware()
class Command(BaseCommand): class Command(BaseCommand):
help = \ help = \
'''Exports all users and user profiles. '''Exports all users and user profiles.
...@@ -34,6 +35,7 @@ for schema changes. ...@@ -34,6 +35,7 @@ for schema changes.
Current version grabs user_keys from Current version grabs user_keys from
django.contrib.auth.models.User and up_keys django.contrib.auth.models.User and up_keys
from student.userprofile. ''' from student.userprofile. '''
def handle(self, *args, **options): def handle(self, *args, **options):
users = list(User.objects.all()) users = list(User.objects.all())
user_profiles = list(UserProfile.objects.all()) user_profiles = list(UserProfile.objects.all())
...@@ -44,7 +46,7 @@ from student.userprofile. ''' ...@@ -44,7 +46,7 @@ from student.userprofile. '''
user_keys = ['id', 'username', 'email', 'password', 'is_staff', user_keys = ['id', 'username', 'email', 'password', 'is_staff',
'is_active', 'is_superuser', 'last_login', 'date_joined', 'is_active', 'is_superuser', 'last_login', 'date_joined',
'password'] 'password']
up_keys = ['language', 'location','meta','name', 'id','user_id'] up_keys = ['language', 'location', 'meta', 'name', 'id', 'user_id']
def extract_dict(keys, object): def extract_dict(keys, object):
d = {} d = {}
......
...@@ -22,6 +22,7 @@ import mitxmako.middleware as middleware ...@@ -22,6 +22,7 @@ import mitxmako.middleware as middleware
middleware.MakoMiddleware() middleware.MakoMiddleware()
def import_user(u): def import_user(u):
user_info = u['u'] user_info = u['u']
up_info = u['up'] up_info = u['up']
...@@ -33,8 +34,7 @@ def import_user(u): ...@@ -33,8 +34,7 @@ def import_user(u):
user_keys = ['id', 'username', 'email', 'password', 'is_staff', user_keys = ['id', 'username', 'email', 'password', 'is_staff',
'is_active', 'is_superuser', 'last_login', 'date_joined', 'is_active', 'is_superuser', 'last_login', 'date_joined',
'password'] 'password']
up_keys = ['language', 'location','meta','name', 'id','user_id'] up_keys = ['language', 'location', 'meta', 'name', 'id', 'user_id']
u = User() u = User()
for key in user_keys: for key in user_keys:
...@@ -47,6 +47,7 @@ def import_user(u): ...@@ -47,6 +47,7 @@ def import_user(u):
up.__setattr__(key, up_info[key]) up.__setattr__(key, up_info[key])
up.save() up.save()
class Command(BaseCommand): class Command(BaseCommand):
help = \ help = \
'''Exports all users and user profiles. '''Exports all users and user profiles.
...@@ -56,11 +57,12 @@ for schema changes. ...@@ -56,11 +57,12 @@ for schema changes.
Current version grabs user_keys from Current version grabs user_keys from
django.contrib.auth.models.User and up_keys django.contrib.auth.models.User and up_keys
from student.userprofile. ''' from student.userprofile. '''
def handle(self, *args, **options): def handle(self, *args, **options):
extracted = json.load(open('transfer_users.txt')) extracted = json.load(open('transfer_users.txt'))
n=0 n = 0
for u in extracted: for u in extracted:
import_user(u) import_user(u)
if n%100 == 0: if n % 100 == 0:
print n print n
n = n+1 n = n + 1
...@@ -17,18 +17,20 @@ import json ...@@ -17,18 +17,20 @@ import json
middleware.MakoMiddleware() middleware.MakoMiddleware()
def group_from_value(groups, v): def group_from_value(groups, v):
''' Given group: (('a',0.3),('b',0.4),('c',0.3)) And random value ''' Given group: (('a',0.3),('b',0.4),('c',0.3)) And random value
in [0,1], return the associated group (in the above case, return in [0,1], return the associated group (in the above case, return
'a' if v<0.3, 'b' if 0.3<=v<0.7, and 'c' if v>0.7 'a' if v<0.3, 'b' if 0.3<=v<0.7, and 'c' if v>0.7
''' '''
sum = 0 sum = 0
for (g,p) in groups: for (g, p) in groups:
sum = sum + p sum = sum + p
if sum > v: if sum > v:
return g return g
return g # For round-off errors return g # For round-off errors
class Command(BaseCommand): class Command(BaseCommand):
help = \ help = \
''' Assign users to test groups. Takes a list ''' Assign users to test groups. Takes a list
...@@ -40,6 +42,7 @@ add up to 1. ...@@ -40,6 +42,7 @@ add up to 1.
Will log what happened to file.txt. Will log what happened to file.txt.
''' '''
def handle(self, *args, **options): def handle(self, *args, **options):
if len(args) != 3: if len(args) != 3:
print "Invalid number of options" print "Invalid number of options"
...@@ -47,13 +50,13 @@ Will log what happened to file.txt. ...@@ -47,13 +50,13 @@ Will log what happened to file.txt.
# Extract groups from string # Extract groups from string
group_strs = [x.split(':') for x in args[0].split(',')] group_strs = [x.split(':') for x in args[0].split(',')]
groups = [(group,float(value)) for group,value in group_strs] groups = [(group, float(value)) for group, value in group_strs]
print "Groups", groups print "Groups", groups
## Confirm group probabilities add up to 1 ## Confirm group probabilities add up to 1
total = sum(zip(*groups)[1]) total = sum(zip(*groups)[1])
print "Total:", total print "Total:", total
if abs(total-1)>0.01: if abs(total - 1) > 0.01:
print "Total not 1" print "Total not 1"
sys.exit(-1) sys.exit(-1)
...@@ -65,15 +68,15 @@ Will log what happened to file.txt. ...@@ -65,15 +68,15 @@ Will log what happened to file.txt.
group_objects = {} group_objects = {}
f = open(args[1],"a+") f = open(args[1], "a+")
## Create groups ## Create groups
for group in dict(groups): for group in dict(groups):
utg = UserTestGroup() utg = UserTestGroup()
utg.name=group utg.name = group
utg.description = json.dumps({"description":args[2]}, utg.description = json.dumps({"description": args[2]},
{"time":datetime.datetime.utcnow().isoformat()}) {"time": datetime.datetime.utcnow().isoformat()})
group_objects[group]=utg group_objects[group] = utg
group_objects[group].save() group_objects[group].save()
## Assign groups ## Assign groups
...@@ -83,8 +86,8 @@ Will log what happened to file.txt. ...@@ -83,8 +86,8 @@ Will log what happened to file.txt.
if count % 1000 == 0: if count % 1000 == 0:
print count print count
count = count + 1 count = count + 1
v = random.uniform(0,1) v = random.uniform(0, 1)
group = group_from_value(groups,v) group = group_from_value(groups, v)
group_objects[group].users.add(user) group_objects[group].users.add(user)
f.write("Assigned user {name} ({id}) to {group}\n".format(name=user.username, f.write("Assigned user {name} ({id}) to {group}\n".format(name=user.username,
id=user.id, id=user.id,
......
...@@ -10,9 +10,11 @@ import mitxmako.middleware as middleware ...@@ -10,9 +10,11 @@ import mitxmako.middleware as middleware
middleware.MakoMiddleware() middleware.MakoMiddleware()
class Command(BaseCommand): class Command(BaseCommand):
help = \ help = \
''' Extract an e-mail list of all active students. ''' ''' Extract an e-mail list of all active students. '''
def handle(self, *args, **options): def handle(self, *args, **options):
#text = open(args[0]).read() #text = open(args[0]).read()
#subject = open(args[1]).read() #subject = open(args[1]).read()
......
...@@ -10,18 +10,20 @@ import mitxmako.middleware as middleware ...@@ -10,18 +10,20 @@ import mitxmako.middleware as middleware
middleware.MakoMiddleware() middleware.MakoMiddleware()
class Command(BaseCommand): class Command(BaseCommand):
help = \ help = \
'''Sends an e-mail to all users. Takes a single '''Sends an e-mail to all users. Takes a single
parameter -- name of e-mail template -- located parameter -- name of e-mail template -- located
in templates/email. Adds a .txt for the message in templates/email. Adds a .txt for the message
body, and an _subject.txt for the subject. ''' body, and an _subject.txt for the subject. '''
def handle(self, *args, **options): def handle(self, *args, **options):
#text = open(args[0]).read() #text = open(args[0]).read()
#subject = open(args[1]).read() #subject = open(args[1]).read()
users = User.objects.all() users = User.objects.all()
text = middleware.lookup['main'].get_template('email/'+args[0]+".txt").render() text = middleware.lookup['main'].get_template('email/' + args[0] + ".txt").render()
subject = middleware.lookup['main'].get_template('email/'+args[0]+"_subject.txt").render().strip() subject = middleware.lookup['main'].get_template('email/' + args[0] + "_subject.txt").render().strip()
for user in users: for user in users:
if user.is_active: if user.is_active:
user.email_user(subject, text) user.email_user(subject, text)
...@@ -16,11 +16,13 @@ import datetime ...@@ -16,11 +16,13 @@ import datetime
middleware.MakoMiddleware() middleware.MakoMiddleware()
def chunks(l, n): def chunks(l, n):
""" Yield successive n-sized chunks from l. """ Yield successive n-sized chunks from l.
""" """
for i in xrange(0, len(l), n): for i in xrange(0, len(l), n):
yield l[i:i+n] yield l[i:i + n]
class Command(BaseCommand): class Command(BaseCommand):
help = \ help = \
...@@ -35,7 +37,7 @@ rate -- messages per second ...@@ -35,7 +37,7 @@ rate -- messages per second
log_file = None log_file = None
def hard_log(self, text): def hard_log(self, text):
self.log_file.write(datetime.datetime.utcnow().isoformat()+' -- '+text+'\n') self.log_file.write(datetime.datetime.utcnow().isoformat() + ' -- ' + text + '\n')
def handle(self, *args, **options): def handle(self, *args, **options):
global log_file global log_file
...@@ -43,20 +45,20 @@ rate -- messages per second ...@@ -43,20 +45,20 @@ rate -- messages per second
users = [u.strip() for u in open(user_file).readlines()] users = [u.strip() for u in open(user_file).readlines()]
message = middleware.lookup['main'].get_template('emails/'+message_base+"_body.txt").render() message = middleware.lookup['main'].get_template('emails/' + message_base + "_body.txt").render()
subject = middleware.lookup['main'].get_template('emails/'+message_base+"_subject.txt").render().strip() subject = middleware.lookup['main'].get_template('emails/' + message_base + "_subject.txt").render().strip()
rate = int(ratestr) rate = int(ratestr)
self.log_file = open(logfilename, "a+", buffering = 0) self.log_file = open(logfilename, "a+", buffering=0)
i=0 i = 0
for users in chunks(users, rate): for users in chunks(users, rate):
emails = [ (subject, message, settings.DEFAULT_FROM_EMAIL, [u]) for u in users ] emails = [(subject, message, settings.DEFAULT_FROM_EMAIL, [u]) for u in users]
self.hard_log(" ".join(users)) self.hard_log(" ".join(users))
send_mass_mail( emails, fail_silently = False ) send_mass_mail(emails, fail_silently=False)
time.sleep(1) time.sleep(1)
print datetime.datetime.utcnow().isoformat(), i print datetime.datetime.utcnow().isoformat(), i
i = i+len(users) i = i + len(users)
# Emergency interruptor # Emergency interruptor
if os.path.exists("/tmp/stopemails.txt"): if os.path.exists("/tmp/stopemails.txt"):
self.log_file.close() self.log_file.close()
......
...@@ -13,26 +13,28 @@ from student.models import UserProfile ...@@ -13,26 +13,28 @@ from student.models import UserProfile
middleware.MakoMiddleware() middleware.MakoMiddleware()
class Command(BaseCommand): class Command(BaseCommand):
help = \ help = \
''' Extract full user information into a JSON file. ''' Extract full user information into a JSON file.
Pass a single filename.''' Pass a single filename.'''
def handle(self, *args, **options): def handle(self, *args, **options):
f = open(args[0],'w') f = open(args[0], 'w')
#text = open(args[0]).read() #text = open(args[0]).read()
#subject = open(args[1]).read() #subject = open(args[1]).read()
users = User.objects.all() users = User.objects.all()
l = [] l = []
for user in users: for user in users:
up = UserProfile.objects.get(user = user) up = UserProfile.objects.get(user=user)
d = { 'username':user.username, d = {'username': user.username,
'email':user.email, 'email': user.email,
'is_active':user.is_active, 'is_active': user.is_active,
'joined':user.date_joined.isoformat(), 'joined': user.date_joined.isoformat(),
'name':up.name, 'name': up.name,
'language':up.language, 'language': up.language,
'location':up.location} 'location': up.location}
l.append(d) l.append(d)
json.dump(l,f) json.dump(l, f)
f.close() f.close()
...@@ -4,6 +4,7 @@ from south.db import db ...@@ -4,6 +4,7 @@ from south.db import db
from south.v2 import SchemaMigration from south.v2 import SchemaMigration
from django.db import models from django.db import models
class Migration(SchemaMigration): class Migration(SchemaMigration):
def forwards(self, orm): def forwards(self, orm):
...@@ -28,7 +29,6 @@ class Migration(SchemaMigration): ...@@ -28,7 +29,6 @@ class Migration(SchemaMigration):
)) ))
db.send_create_signal('student', ['Registration']) db.send_create_signal('student', ['Registration'])
def backwards(self, orm): def backwards(self, orm):
# Deleting model 'UserProfile' # Deleting model 'UserProfile'
...@@ -37,7 +37,6 @@ class Migration(SchemaMigration): ...@@ -37,7 +37,6 @@ class Migration(SchemaMigration):
# Deleting model 'Registration' # Deleting model 'Registration'
db.delete_table('auth_registration') db.delete_table('auth_registration')
models = { models = {
'auth.group': { 'auth.group': {
'Meta': {'object_name': 'Group'}, 'Meta': {'object_name': 'Group'},
......
...@@ -4,6 +4,7 @@ from south.db import db ...@@ -4,6 +4,7 @@ from south.db import db
from south.v2 import SchemaMigration from south.v2 import SchemaMigration
from django.db import models from django.db import models
class Migration(SchemaMigration): class Migration(SchemaMigration):
def forwards(self, orm): def forwards(self, orm):
...@@ -32,7 +33,6 @@ class Migration(SchemaMigration): ...@@ -32,7 +33,6 @@ class Migration(SchemaMigration):
# Adding index on 'UserProfile', fields ['location'] # Adding index on 'UserProfile', fields ['location']
db.create_index('auth_userprofile', ['location']) db.create_index('auth_userprofile', ['location'])
def backwards(self, orm): def backwards(self, orm):
# Removing index on 'UserProfile', fields ['location'] # Removing index on 'UserProfile', fields ['location']
...@@ -59,7 +59,6 @@ class Migration(SchemaMigration): ...@@ -59,7 +59,6 @@ class Migration(SchemaMigration):
# Changing field 'UserProfile.location' # Changing field 'UserProfile.location'
db.alter_column('auth_userprofile', 'location', self.gf('django.db.models.fields.TextField')()) db.alter_column('auth_userprofile', 'location', self.gf('django.db.models.fields.TextField')())
models = { models = {
'auth.group': { 'auth.group': {
'Meta': {'object_name': 'Group'}, 'Meta': {'object_name': 'Group'},
......
...@@ -4,6 +4,7 @@ from south.db import db ...@@ -4,6 +4,7 @@ from south.db import db
from south.v2 import SchemaMigration from south.v2 import SchemaMigration
from django.db import models from django.db import models
class Migration(SchemaMigration): class Migration(SchemaMigration):
def forwards(self, orm): def forwards(self, orm):
...@@ -24,7 +25,6 @@ class Migration(SchemaMigration): ...@@ -24,7 +25,6 @@ class Migration(SchemaMigration):
)) ))
db.create_unique('student_usertestgroup_users', ['usertestgroup_id', 'user_id']) db.create_unique('student_usertestgroup_users', ['usertestgroup_id', 'user_id'])
def backwards(self, orm): def backwards(self, orm):
# Deleting model 'UserTestGroup' # Deleting model 'UserTestGroup'
...@@ -33,7 +33,6 @@ class Migration(SchemaMigration): ...@@ -33,7 +33,6 @@ class Migration(SchemaMigration):
# Removing M2M table for field users on 'UserTestGroup' # Removing M2M table for field users on 'UserTestGroup'
db.delete_table('student_usertestgroup_users') db.delete_table('student_usertestgroup_users')
models = { models = {
'auth.group': { 'auth.group': {
'Meta': {'object_name': 'Group'}, 'Meta': {'object_name': 'Group'},
......
...@@ -4,18 +4,17 @@ from south.db import db ...@@ -4,18 +4,17 @@ from south.db import db
from south.v2 import SchemaMigration from south.v2 import SchemaMigration
from django.db import models from django.db import models
class Migration(SchemaMigration): class Migration(SchemaMigration):
def forwards(self, orm): def forwards(self, orm):
db.execute("create unique index email on auth_user (email)") db.execute("create unique index email on auth_user (email)")
pass pass
def backwards(self, orm): def backwards(self, orm):
db.execute("drop index email on auth_user") db.execute("drop index email on auth_user")
pass pass
models = { models = {
'auth.group': { 'auth.group': {
'Meta': {'object_name': 'Group'}, 'Meta': {'object_name': 'Group'},
......
...@@ -4,6 +4,7 @@ from south.db import db ...@@ -4,6 +4,7 @@ from south.db import db
from south.v2 import SchemaMigration from south.v2 import SchemaMigration
from django.db import models from django.db import models
class Migration(SchemaMigration): class Migration(SchemaMigration):
def forwards(self, orm): def forwards(self, orm):
...@@ -29,7 +30,6 @@ class Migration(SchemaMigration): ...@@ -29,7 +30,6 @@ class Migration(SchemaMigration):
# Changing field 'UserProfile.user' # Changing field 'UserProfile.user'
db.alter_column('auth_userprofile', 'user_id', self.gf('django.db.models.fields.related.OneToOneField')(unique=True, to=orm['auth.User'])) db.alter_column('auth_userprofile', 'user_id', self.gf('django.db.models.fields.related.OneToOneField')(unique=True, to=orm['auth.User']))
def backwards(self, orm): def backwards(self, orm):
# Deleting model 'PendingEmailChange' # Deleting model 'PendingEmailChange'
...@@ -41,7 +41,6 @@ class Migration(SchemaMigration): ...@@ -41,7 +41,6 @@ class Migration(SchemaMigration):
# Changing field 'UserProfile.user' # Changing field 'UserProfile.user'
db.alter_column('auth_userprofile', 'user_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], unique=True)) db.alter_column('auth_userprofile', 'user_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], unique=True))
models = { models = {
'auth.group': { 'auth.group': {
'Meta': {'object_name': 'Group'}, 'Meta': {'object_name': 'Group'},
......
...@@ -4,6 +4,7 @@ from south.db import db ...@@ -4,6 +4,7 @@ from south.db import db
from south.v2 import SchemaMigration from south.v2 import SchemaMigration
from django.db import models from django.db import models
class Migration(SchemaMigration): class Migration(SchemaMigration):
def forwards(self, orm): def forwards(self, orm):
...@@ -11,13 +12,11 @@ class Migration(SchemaMigration): ...@@ -11,13 +12,11 @@ class Migration(SchemaMigration):
# Changing field 'UserProfile.meta' # Changing field 'UserProfile.meta'
db.alter_column('auth_userprofile', 'meta', self.gf('django.db.models.fields.TextField')()) db.alter_column('auth_userprofile', 'meta', self.gf('django.db.models.fields.TextField')())
def backwards(self, orm): def backwards(self, orm):
# Changing field 'UserProfile.meta' # Changing field 'UserProfile.meta'
db.alter_column('auth_userprofile', 'meta', self.gf('django.db.models.fields.CharField')(max_length=255)) db.alter_column('auth_userprofile', 'meta', self.gf('django.db.models.fields.CharField')(max_length=255))
models = { models = {
'auth.group': { 'auth.group': {
'Meta': {'object_name': 'Group'}, 'Meta': {'object_name': 'Group'},
......
...@@ -4,6 +4,7 @@ from south.db import db ...@@ -4,6 +4,7 @@ from south.db import db
from south.v2 import SchemaMigration from south.v2 import SchemaMigration
from django.db import models from django.db import models
class Migration(SchemaMigration): class Migration(SchemaMigration):
def forwards(self, orm): def forwards(self, orm):
...@@ -16,12 +17,10 @@ class Migration(SchemaMigration): ...@@ -16,12 +17,10 @@ class Migration(SchemaMigration):
ALTER TABLE student_usertestgroup_users CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci; ALTER TABLE student_usertestgroup_users CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;
""") """)
def backwards(self, orm): def backwards(self, orm):
# Although this migration can't be undone, it is okay for it to be run backwards because it doesn't add/remove any fields # Although this migration can't be undone, it is okay for it to be run backwards because it doesn't add/remove any fields
pass pass
models = { models = {
'auth.group': { 'auth.group': {
'Meta': {'object_name': 'Group'}, 'Meta': {'object_name': 'Group'},
......
...@@ -16,12 +16,10 @@ class Migration(SchemaMigration): ...@@ -16,12 +16,10 @@ class Migration(SchemaMigration):
)) ))
db.send_create_signal('student', ['CourseRegistration']) db.send_create_signal('student', ['CourseRegistration'])
def backwards(self, orm): def backwards(self, orm):
# Deleting model 'CourseRegistration' # Deleting model 'CourseRegistration'
db.delete_table('student_courseregistration') db.delete_table('student_courseregistration')
models = { models = {
'auth.group': { 'auth.group': {
'Meta': {'object_name': 'Group'}, 'Meta': {'object_name': 'Group'},
......
...@@ -19,7 +19,6 @@ class Migration(SchemaMigration): ...@@ -19,7 +19,6 @@ class Migration(SchemaMigration):
)) ))
db.send_create_signal('student', ['CourseEnrollment']) db.send_create_signal('student', ['CourseEnrollment'])
def backwards(self, orm): def backwards(self, orm):
# Adding model 'CourseRegistration' # Adding model 'CourseRegistration'
db.create_table('student_courseregistration', ( db.create_table('student_courseregistration', (
...@@ -32,7 +31,6 @@ class Migration(SchemaMigration): ...@@ -32,7 +31,6 @@ class Migration(SchemaMigration):
# Deleting model 'CourseEnrollment' # Deleting model 'CourseEnrollment'
db.delete_table('student_courseenrollment') db.delete_table('student_courseenrollment')
models = { models = {
'auth.group': { 'auth.group': {
'Meta': {'object_name': 'Group'}, 'Meta': {'object_name': 'Group'},
......
...@@ -25,7 +25,6 @@ class Migration(SchemaMigration): ...@@ -25,7 +25,6 @@ class Migration(SchemaMigration):
# # Adding unique constraint on 'CourseEnrollment', fields ['user'] # # Adding unique constraint on 'CourseEnrollment', fields ['user']
# db.create_unique('student_courseenrollment', ['user_id']) # db.create_unique('student_courseenrollment', ['user_id'])
models = { models = {
'auth.group': { 'auth.group': {
'Meta': {'object_name': 'Group'}, 'Meta': {'object_name': 'Group'},
......
...@@ -38,7 +38,6 @@ class Migration(SchemaMigration): ...@@ -38,7 +38,6 @@ class Migration(SchemaMigration):
self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True), self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True),
keep_default=False) keep_default=False)
def backwards(self, orm): def backwards(self, orm):
# Deleting field 'UserProfile.gender' # Deleting field 'UserProfile.gender'
db.delete_column('auth_userprofile', 'gender') db.delete_column('auth_userprofile', 'gender')
...@@ -58,7 +57,6 @@ class Migration(SchemaMigration): ...@@ -58,7 +57,6 @@ class Migration(SchemaMigration):
# Deleting field 'UserProfile.occupation' # Deleting field 'UserProfile.occupation'
db.delete_column('auth_userprofile', 'occupation') db.delete_column('auth_userprofile', 'occupation')
models = { models = {
'auth.group': { 'auth.group': {
'Meta': {'object_name': 'Group'}, 'Meta': {'object_name': 'Group'},
......
...@@ -11,7 +11,6 @@ class Migration(SchemaMigration): ...@@ -11,7 +11,6 @@ class Migration(SchemaMigration):
# Deleting model 'CourseEnrollment' # Deleting model 'CourseEnrollment'
db.delete_table('student_courseenrollment') db.delete_table('student_courseenrollment')
def backwards(self, orm): def backwards(self, orm):
# Adding model 'CourseEnrollment' # Adding model 'CourseEnrollment'
db.create_table('student_courseenrollment', ( db.create_table('student_courseenrollment', (
...@@ -21,7 +20,6 @@ class Migration(SchemaMigration): ...@@ -21,7 +20,6 @@ class Migration(SchemaMigration):
)) ))
db.send_create_signal('student', ['CourseEnrollment']) db.send_create_signal('student', ['CourseEnrollment'])
models = { models = {
'auth.group': { 'auth.group': {
'Meta': {'object_name': 'Group'}, 'Meta': {'object_name': 'Group'},
......
...@@ -19,7 +19,6 @@ class Migration(SchemaMigration): ...@@ -19,7 +19,6 @@ class Migration(SchemaMigration):
# Adding unique constraint on 'CourseEnrollment', fields ['user', 'course_id'] # Adding unique constraint on 'CourseEnrollment', fields ['user', 'course_id']
db.create_unique('student_courseenrollment', ['user_id', 'course_id']) db.create_unique('student_courseenrollment', ['user_id', 'course_id'])
def backwards(self, orm): def backwards(self, orm):
# Removing unique constraint on 'CourseEnrollment', fields ['user', 'course_id'] # Removing unique constraint on 'CourseEnrollment', fields ['user', 'course_id']
db.delete_unique('student_courseenrollment', ['user_id', 'course_id']) db.delete_unique('student_courseenrollment', ['user_id', 'course_id'])
...@@ -27,7 +26,6 @@ class Migration(SchemaMigration): ...@@ -27,7 +26,6 @@ class Migration(SchemaMigration):
# Deleting model 'CourseEnrollment' # Deleting model 'CourseEnrollment'
db.delete_table('student_courseenrollment') db.delete_table('student_courseenrollment')
models = { models = {
'auth.group': { 'auth.group': {
'Meta': {'object_name': 'Group'}, 'Meta': {'object_name': 'Group'},
......
...@@ -13,7 +13,6 @@ class Migration(SchemaMigration): ...@@ -13,7 +13,6 @@ class Migration(SchemaMigration):
self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, null=True, blank=True), self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, null=True, blank=True),
keep_default=False) keep_default=False)
# Changing field 'UserProfile.country' # Changing field 'UserProfile.country'
db.alter_column('auth_userprofile', 'country', self.gf('django_countries.fields.CountryField')(max_length=2, null=True)) db.alter_column('auth_userprofile', 'country', self.gf('django_countries.fields.CountryField')(max_length=2, null=True))
...@@ -21,7 +20,6 @@ class Migration(SchemaMigration): ...@@ -21,7 +20,6 @@ class Migration(SchemaMigration):
# Deleting field 'CourseEnrollment.date' # Deleting field 'CourseEnrollment.date'
db.delete_column('student_courseenrollment', 'date') db.delete_column('student_courseenrollment', 'date')
# Changing field 'UserProfile.country' # Changing field 'UserProfile.country'
db.alter_column('auth_userprofile', 'country', self.gf('django.db.models.fields.CharField')(max_length=255, null=True)) db.alter_column('auth_userprofile', 'country', self.gf('django.db.models.fields.CharField')(max_length=255, null=True))
......
...@@ -15,7 +15,6 @@ class Migration(SchemaMigration): ...@@ -15,7 +15,6 @@ class Migration(SchemaMigration):
# Rename 'created' field to 'date' # Rename 'created' field to 'date'
db.rename_column('student_courseenrollment', 'created', 'date') db.rename_column('student_courseenrollment', 'created', 'date')
models = { models = {
'auth.group': { 'auth.group': {
'Meta': {'object_name': 'Group'}, 'Meta': {'object_name': 'Group'},
......
...@@ -11,12 +11,10 @@ class Migration(SchemaMigration): ...@@ -11,12 +11,10 @@ class Migration(SchemaMigration):
# Adding index on 'CourseEnrollment', fields ['created'] # Adding index on 'CourseEnrollment', fields ['created']
db.create_index('student_courseenrollment', ['created']) db.create_index('student_courseenrollment', ['created'])
def backwards(self, orm): def backwards(self, orm):
# Removing index on 'CourseEnrollment', fields ['created'] # Removing index on 'CourseEnrollment', fields ['created']
db.delete_index('student_courseenrollment', ['created']) db.delete_index('student_courseenrollment', ['created'])
models = { models = {
'auth.group': { 'auth.group': {
'Meta': {'object_name': 'Group'}, 'Meta': {'object_name': 'Group'},
......
...@@ -38,7 +38,6 @@ class Migration(SchemaMigration): ...@@ -38,7 +38,6 @@ class Migration(SchemaMigration):
# Adding index on 'UserProfile', fields ['gender'] # Adding index on 'UserProfile', fields ['gender']
db.create_index('auth_userprofile', ['gender']) db.create_index('auth_userprofile', ['gender'])
def backwards(self, orm): def backwards(self, orm):
# Removing index on 'UserProfile', fields ['gender'] # Removing index on 'UserProfile', fields ['gender']
db.delete_index('auth_userprofile', ['gender']) db.delete_index('auth_userprofile', ['gender'])
...@@ -72,7 +71,6 @@ class Migration(SchemaMigration): ...@@ -72,7 +71,6 @@ class Migration(SchemaMigration):
# Deleting field 'UserProfile.goals' # Deleting field 'UserProfile.goals'
db.delete_column('auth_userprofile', 'goals') db.delete_column('auth_userprofile', 'goals')
models = { models = {
'auth.group': { 'auth.group': {
'Meta': {'object_name': 'Group'}, 'Meta': {'object_name': 'Group'},
......
...@@ -18,6 +18,7 @@ from django_countries import CountryField ...@@ -18,6 +18,7 @@ from django_countries import CountryField
#from cache_toolbox import cache_model, cache_relation #from cache_toolbox import cache_model, cache_relation
class UserProfile(models.Model): class UserProfile(models.Model):
class Meta: class Meta:
db_table = "auth_userprofile" db_table = "auth_userprofile"
...@@ -59,7 +60,6 @@ class UserProfile(models.Model): ...@@ -59,7 +60,6 @@ class UserProfile(models.Model):
mailing_address = models.TextField(blank=True, null=True) mailing_address = models.TextField(blank=True, null=True)
goals = models.TextField(blank=True, null=True) goals = models.TextField(blank=True, null=True)
def get_meta(self): def get_meta(self):
js_str = self.meta js_str = self.meta
if not js_str: if not js_str:
...@@ -69,9 +69,10 @@ class UserProfile(models.Model): ...@@ -69,9 +69,10 @@ class UserProfile(models.Model):
return js_str return js_str
def set_meta(self,js): def set_meta(self, js):
self.meta = json.dumps(js) self.meta = json.dumps(js)
## TODO: Should be renamed to generic UserGroup, and possibly ## TODO: Should be renamed to generic UserGroup, and possibly
# Given an optional field for type of group # Given an optional field for type of group
class UserTestGroup(models.Model): class UserTestGroup(models.Model):
...@@ -79,6 +80,7 @@ class UserTestGroup(models.Model): ...@@ -79,6 +80,7 @@ class UserTestGroup(models.Model):
name = models.CharField(blank=False, max_length=32, db_index=True) name = models.CharField(blank=False, max_length=32, db_index=True)
description = models.TextField(blank=True) description = models.TextField(blank=True)
class Registration(models.Model): class Registration(models.Model):
''' Allows us to wait for e-mail before user is registered. A ''' Allows us to wait for e-mail before user is registered. A
registration profile is created when the user creates an registration profile is created when the user creates an
...@@ -92,8 +94,8 @@ class Registration(models.Model): ...@@ -92,8 +94,8 @@ class Registration(models.Model):
def register(self, user): def register(self, user):
# MINOR TODO: Switch to crypto-secure key # MINOR TODO: Switch to crypto-secure key
self.activation_key=uuid.uuid4().hex self.activation_key = uuid.uuid4().hex
self.user=user self.user = user
self.save() self.save()
def activate(self): def activate(self):
...@@ -101,16 +103,19 @@ class Registration(models.Model): ...@@ -101,16 +103,19 @@ class Registration(models.Model):
self.user.save() self.user.save()
#self.delete() #self.delete()
class PendingNameChange(models.Model): class PendingNameChange(models.Model):
user = models.OneToOneField(User, unique=True, db_index=True) user = models.OneToOneField(User, unique=True, db_index=True)
new_name = models.CharField(blank=True, max_length=255) new_name = models.CharField(blank=True, max_length=255)
rationale = models.CharField(blank=True, max_length=1024) rationale = models.CharField(blank=True, max_length=1024)
class PendingEmailChange(models.Model): class PendingEmailChange(models.Model):
user = models.OneToOneField(User, unique=True, db_index=True) user = models.OneToOneField(User, unique=True, db_index=True)
new_email = models.CharField(blank=True, max_length=255, db_index=True) new_email = models.CharField(blank=True, max_length=255, db_index=True)
activation_key = models.CharField(('activation key'), max_length=32, unique=True, db_index=True) activation_key = models.CharField(('activation key'), max_length=32, unique=True, db_index=True)
class CourseEnrollment(models.Model): class CourseEnrollment(models.Model):
user = models.ForeignKey(User) user = models.ForeignKey(User)
course_id = models.CharField(max_length=255, db_index=True) course_id = models.CharField(max_length=255, db_index=True)
...@@ -124,38 +129,45 @@ class CourseEnrollment(models.Model): ...@@ -124,38 +129,45 @@ class CourseEnrollment(models.Model):
#### Helper methods for use from python manage.py shell. #### Helper methods for use from python manage.py shell.
def get_user(email): def get_user(email):
u = User.objects.get(email = email) u = User.objects.get(email=email)
up = UserProfile.objects.get(user = u) up = UserProfile.objects.get(user=u)
return u,up return u, up
def user_info(email): def user_info(email):
u,up = get_user(email) u, up = get_user(email)
print "User id", u.id print "User id", u.id
print "Username", u.username print "Username", u.username
print "E-mail", u.email print "E-mail", u.email
print "Name", up.name print "Name", up.name
print "Location", up.location print "Location", up.location
print "Language", up.language print "Language", up.language
return u,up return u, up
def change_email(old_email, new_email): def change_email(old_email, new_email):
u = User.objects.get(email = old_email) u = User.objects.get(email=old_email)
u.email = new_email u.email = new_email
u.save() u.save()
def change_name(email, new_name): def change_name(email, new_name):
u,up = get_user(email) u, up = get_user(email)
up.name = new_name up.name = new_name
up.save() up.save()
def user_count(): def user_count():
print "All users", User.objects.all().count() print "All users", User.objects.all().count()
print "Active users", User.objects.filter(is_active = True).count() print "Active users", User.objects.filter(is_active=True).count()
return User.objects.all().count() return User.objects.all().count()
def active_user_count(): def active_user_count():
return User.objects.filter(is_active = True).count() return User.objects.filter(is_active=True).count()
def create_group(name, description): def create_group(name, description):
utg = UserTestGroup() utg = UserTestGroup()
...@@ -163,29 +175,31 @@ def create_group(name, description): ...@@ -163,29 +175,31 @@ def create_group(name, description):
utg.description = description utg.description = description
utg.save() utg.save()
def add_user_to_group(user, group): def add_user_to_group(user, group):
utg = UserTestGroup.objects.get(name = group) utg = UserTestGroup.objects.get(name=group)
utg.users.add(User.objects.get(username = user)) utg.users.add(User.objects.get(username=user))
utg.save() utg.save()
def remove_user_from_group(user, group): def remove_user_from_group(user, group):
utg = UserTestGroup.objects.get(name = group) utg = UserTestGroup.objects.get(name=group)
utg.users.remove(User.objects.get(username = user)) utg.users.remove(User.objects.get(username=user))
utg.save() utg.save()
default_groups = {'email_future_courses' : 'Receive e-mails about future MITx courses', default_groups = {'email_future_courses': 'Receive e-mails about future MITx courses',
'email_helpers' : 'Receive e-mails about how to help with MITx', 'email_helpers': 'Receive e-mails about how to help with MITx',
'mitx_unenroll' : 'Fully unenrolled -- no further communications', 'mitx_unenroll': 'Fully unenrolled -- no further communications',
'6002x_unenroll' : 'Took and dropped 6002x'} '6002x_unenroll': 'Took and dropped 6002x'}
def add_user_to_default_group(user, group): def add_user_to_default_group(user, group):
try: try:
utg = UserTestGroup.objects.get(name = group) utg = UserTestGroup.objects.get(name=group)
except UserTestGroup.DoesNotExist: except UserTestGroup.DoesNotExist:
utg = UserTestGroup() utg = UserTestGroup()
utg.name = group utg.name = group
utg.description = default_groups[group] utg.description = default_groups[group]
utg.save() utg.save()
utg.users.add(User.objects.get(username = user)) utg.users.add(User.objects.get(username=user))
utg.save() utg.save()
...@@ -4,6 +4,7 @@ from django.conf import settings ...@@ -4,6 +4,7 @@ from django.conf import settings
import views import views
class TrackMiddleware: class TrackMiddleware:
def process_request(self, request): def process_request(self, request):
try: try:
...@@ -32,12 +33,12 @@ class TrackMiddleware: ...@@ -32,12 +33,12 @@ class TrackMiddleware:
get_dict = dict(request.GET) get_dict = dict(request.GET)
for string in censored_strings: for string in censored_strings:
if string in post_dict: if string in post_dict:
post_dict[string] = '*'*8 post_dict[string] = '*' * 8
if string in get_dict: if string in get_dict:
get_dict[string] = '*'*8 get_dict[string] = '*' * 8
event = { 'GET' : dict(get_dict), event = {'GET': dict(get_dict),
'POST' : dict(post_dict)} 'POST': dict(post_dict)}
# TODO: Confirm no large file uploads # TODO: Confirm no large file uploads
event = json.dumps(event) event = json.dumps(event)
......
...@@ -10,10 +10,12 @@ from django.conf import settings ...@@ -10,10 +10,12 @@ from django.conf import settings
log = logging.getLogger("tracking") log = logging.getLogger("tracking")
def log_event(event): def log_event(event):
event_str = json.dumps(event) event_str = json.dumps(event)
log.info(event_str[:settings.TRACK_MAX_EVENT]) log.info(event_str[:settings.TRACK_MAX_EVENT])
def user_track(request): def user_track(request):
try: # TODO: Do the same for many of the optional META parameters try: # TODO: Do the same for many of the optional META parameters
username = request.user.username username = request.user.username
...@@ -33,19 +35,20 @@ def user_track(request): ...@@ -33,19 +35,20 @@ def user_track(request):
# TODO: Move a bunch of this into log_event # TODO: Move a bunch of this into log_event
event = { event = {
"username" : username, "username": username,
"session" : scookie, "session": scookie,
"ip" : request.META['REMOTE_ADDR'], "ip": request.META['REMOTE_ADDR'],
"event_source" : "browser", "event_source": "browser",
"event_type" : request.GET['event_type'], "event_type": request.GET['event_type'],
"event" : request.GET['event'], "event": request.GET['event'],
"agent" : agent, "agent": agent,
"page" : request.GET['page'], "page": request.GET['page'],
"time": datetime.datetime.utcnow().isoformat(), "time": datetime.datetime.utcnow().isoformat(),
} }
log_event(event) log_event(event)
return HttpResponse('success') return HttpResponse('success')
def server_track(request, event_type, event, page=None): def server_track(request, event_type, event, page=None):
try: try:
username = request.user.username username = request.user.username
...@@ -58,13 +61,13 @@ def server_track(request, event_type, event, page=None): ...@@ -58,13 +61,13 @@ def server_track(request, event_type, event, page=None):
agent = '' agent = ''
event = { event = {
"username" : username, "username": username,
"ip" : request.META['REMOTE_ADDR'], "ip": request.META['REMOTE_ADDR'],
"event_source" : "server", "event_source": "server",
"event_type" : event_type, "event_type": event_type,
"event" : event, "event": event,
"agent" : agent, "agent": agent,
"page" : page, "page": page,
"time": datetime.datetime.utcnow().isoformat(), "time": datetime.datetime.utcnow().isoformat(),
} }
log_event(event) log_event(event)
...@@ -16,6 +16,7 @@ try: ...@@ -16,6 +16,7 @@ try:
except Exception: except Exception:
cache = cache.cache cache = cache.cache
def cache_if_anonymous(view_func): def cache_if_anonymous(view_func):
""" """
Many of the pages in edX are identical when the user is not logged Many of the pages in edX are identical when the user is not logged
......
...@@ -2,6 +2,7 @@ from functools import wraps ...@@ -2,6 +2,7 @@ from functools import wraps
import copy import copy
import json import json
def expect_json(view_function): def expect_json(view_function):
@wraps(view_function) @wraps(view_function)
def expect_json_with_cloned_request(request, *args, **kwargs): def expect_json_with_cloned_request(request, *args, **kwargs):
......
...@@ -6,11 +6,13 @@ from django.utils.encoding import smart_str ...@@ -6,11 +6,13 @@ from django.utils.encoding import smart_str
import hashlib import hashlib
import urllib import urllib
def fasthash(string): def fasthash(string):
m = hashlib.new("md4") m = hashlib.new("md4")
m.update(string) m.update(string)
return m.hexdigest() return m.hexdigest()
def safe_key(key, key_prefix, version): def safe_key(key, key_prefix, version):
safe_key = urllib.quote_plus(smart_str(key)) safe_key = urllib.quote_plus(smart_str(key))
......
...@@ -5,6 +5,7 @@ from django.http import HttpResponseServerError ...@@ -5,6 +5,7 @@ from django.http import HttpResponseServerError
log = logging.getLogger("mitx") log = logging.getLogger("mitx")
class ExceptionLoggingMiddleware(object): class ExceptionLoggingMiddleware(object):
"""Just here to log unchecked exceptions that go all the way up the Django """Just here to log unchecked exceptions that go all the way up the Django
stack""" stack"""
...@@ -13,4 +14,3 @@ class ExceptionLoggingMiddleware(object): ...@@ -13,4 +14,3 @@ class ExceptionLoggingMiddleware(object):
def process_exception(self, request, exception): def process_exception(self, request, exception):
log.exception(exception) log.exception(exception)
return HttpResponseServerError("Server Error - Please try again later.") return HttpResponseServerError("Server Error - Please try again later.")
...@@ -14,17 +14,19 @@ from mitxmako.shortcuts import render_to_response, render_to_string ...@@ -14,17 +14,19 @@ from mitxmako.shortcuts import render_to_response, render_to_string
import capa.calc import capa.calc
import track.views import track.views
def calculate(request): def calculate(request):
''' Calculator in footer of every page. ''' ''' Calculator in footer of every page. '''
equation = request.GET['equation'] equation = request.GET['equation']
try: try:
result = capa.calc.evaluator({}, {}, equation) result = capa.calc.evaluator({}, {}, equation)
except: except:
event = {'error':map(str,sys.exc_info()), event = {'error': map(str, sys.exc_info()),
'equation':equation} 'equation': equation}
track.views.server_track(request, 'error:calc', event, page='calc') track.views.server_track(request, 'error:calc', event, page='calc')
return HttpResponse(json.dumps({'result':'Invalid syntax'})) return HttpResponse(json.dumps({'result': 'Invalid syntax'}))
return HttpResponse(json.dumps({'result':str(result)})) return HttpResponse(json.dumps({'result': str(result)}))
def send_feedback(request): def send_feedback(request):
''' Feeback mechanism in footer of every page. ''' ''' Feeback mechanism in footer of every page. '''
...@@ -41,26 +43,28 @@ def send_feedback(request): ...@@ -41,26 +43,28 @@ def send_feedback(request):
browser = "Unknown" browser = "Unknown"
feedback = render_to_string("feedback_email.txt", feedback = render_to_string("feedback_email.txt",
{"subject":request.POST['subject'], {"subject": request.POST['subject'],
"url": request.POST['url'], "url": request.POST['url'],
"time": datetime.datetime.now().isoformat(), "time": datetime.datetime.now().isoformat(),
"feedback": request.POST['message'], "feedback": request.POST['message'],
"email":email, "email": email,
"browser":browser, "browser": browser,
"user":username}) "user": username})
send_mail("MITx Feedback / " +request.POST['subject'], send_mail("MITx Feedback / " + request.POST['subject'],
feedback, feedback,
settings.DEFAULT_FROM_EMAIL, settings.DEFAULT_FROM_EMAIL,
[ settings.DEFAULT_FEEDBACK_EMAIL ], [settings.DEFAULT_FEEDBACK_EMAIL],
fail_silently = False fail_silently=False
) )
return HttpResponse(json.dumps({'success':True})) return HttpResponse(json.dumps({'success': True}))
def info(request): def info(request):
''' Info page (link from main header) ''' ''' Info page (link from main header) '''
return render_to_response("info.html", {}) return render_to_response("info.html", {})
# From http://djangosnippets.org/snippets/1042/ # From http://djangosnippets.org/snippets/1042/
def parse_accept_header(accept): def parse_accept_header(accept):
"""Parse the Accept header *accept*, returning a list with pairs of """Parse the Accept header *accept*, returning a list with pairs of
...@@ -82,6 +86,7 @@ def parse_accept_header(accept): ...@@ -82,6 +86,7 @@ def parse_accept_header(accept):
result.sort(lambda x, y: -cmp(x[2], y[2])) result.sort(lambda x, y: -cmp(x[2], y[2]))
return result return result
def accepts(request, media_type): def accepts(request, media_type):
"""Return whether this request has an Accept header that matches type""" """Return whether this request has an Accept header that matches type"""
accept = parse_accept_header(request.META.get("HTTP_ACCEPT", "")) accept = parse_accept_header(request.META.get("HTTP_ACCEPT", ""))
......
...@@ -37,7 +37,7 @@ from util import contextualize_text ...@@ -37,7 +37,7 @@ from util import contextualize_text
import responsetypes import responsetypes
# dict of tagname, Response Class -- this should come from auto-registering # dict of tagname, Response Class -- this should come from auto-registering
response_tag_dict = dict([(x.response_tag,x) for x in responsetypes.__all__]) response_tag_dict = dict([(x.response_tag, x) for x in responsetypes.__all__])
entry_types = ['textline', 'schematic', 'textbox', 'imageinput', 'optioninput', 'choicegroup', 'radiogroup', 'checkboxgroup'] entry_types = ['textline', 'schematic', 'textbox', 'imageinput', 'optioninput', 'choicegroup', 'radiogroup', 'checkboxgroup']
solution_types = ['solution'] # extra things displayed after "show answers" is pressed solution_types = ['solution'] # extra things displayed after "show answers" is pressed
...@@ -57,13 +57,14 @@ global_context = {'random': random, ...@@ -57,13 +57,14 @@ global_context = {'random': random,
'eia': eia} 'eia': eia}
# These should be removed from HTML output, including all subelements # These should be removed from HTML output, including all subelements
html_problem_semantics = ["responseparam", "answer", "script","hintgroup"] html_problem_semantics = ["responseparam", "answer", "script", "hintgroup"]
log = logging.getLogger('mitx.' + __name__) log = logging.getLogger('mitx.' + __name__)
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# main class for this module # main class for this module
class LoncapaProblem(object): class LoncapaProblem(object):
''' '''
Main class for capa Problems. Main class for capa Problems.
...@@ -132,7 +133,7 @@ class LoncapaProblem(object): ...@@ -132,7 +133,7 @@ class LoncapaProblem(object):
def set_initial_display(self): def set_initial_display(self):
initial_answers = dict() initial_answers = dict()
for responder in self.responders.values(): for responder in self.responders.values():
if hasattr(responder,'get_initial_display'): if hasattr(responder, 'get_initial_display'):
initial_answers.update(responder.get_initial_display()) initial_answers.update(responder.get_initial_display())
self.student_answers = initial_answers self.student_answers = initial_answers
...@@ -160,7 +161,7 @@ class LoncapaProblem(object): ...@@ -160,7 +161,7 @@ class LoncapaProblem(object):
''' '''
maxscore = 0 maxscore = 0
for response, responder in self.responders.iteritems(): for response, responder in self.responders.iteritems():
if hasattr(responder,'get_max_score'): if hasattr(responder, 'get_max_score'):
try: try:
maxscore += responder.get_max_score() maxscore += responder.get_max_score()
except Exception: except Exception:
...@@ -181,7 +182,7 @@ class LoncapaProblem(object): ...@@ -181,7 +182,7 @@ class LoncapaProblem(object):
try: try:
correct += self.correct_map.get_npoints(key) correct += self.correct_map.get_npoints(key)
except Exception: except Exception:
log.error('key=%s, correct_map = %s' % (key,self.correct_map)) log.error('key=%s, correct_map = %s' % (key, self.correct_map))
raise raise
if (not self.student_answers) or len(self.student_answers) == 0: if (not self.student_answers) or len(self.student_answers) == 0:
...@@ -201,7 +202,7 @@ class LoncapaProblem(object): ...@@ -201,7 +202,7 @@ class LoncapaProblem(object):
cmap = CorrectMap() cmap = CorrectMap()
cmap.update(self.correct_map) cmap.update(self.correct_map)
for responder in self.responders.values(): for responder in self.responders.values():
if hasattr(responder,'update_score'): if hasattr(responder, 'update_score'):
# Each LoncapaResponse will update the specific entries of 'cmap' that it's responsible for # Each LoncapaResponse will update the specific entries of 'cmap' that it's responsible for
cmap = responder.update_score(score_msg, cmap, queuekey) cmap = responder.update_score(score_msg, cmap, queuekey)
self.correct_map.set_dict(cmap.get_dict()) self.correct_map.set_dict(cmap.get_dict())
...@@ -232,7 +233,7 @@ class LoncapaProblem(object): ...@@ -232,7 +233,7 @@ class LoncapaProblem(object):
newcmap = CorrectMap() # start new with empty CorrectMap newcmap = CorrectMap() # start new with empty CorrectMap
# log.debug('Responders: %s' % self.responders) # log.debug('Responders: %s' % self.responders)
for responder in self.responders.values(): for responder in self.responders.values():
results = responder.evaluate_answers(answers,oldcmap) # call the responsetype instance to do the actual grading results = responder.evaluate_answers(answers, oldcmap) # call the responsetype instance to do the actual grading
newcmap.update(results) newcmap.update(results)
self.correct_map = newcmap self.correct_map = newcmap
# log.debug('%s: in grade_answers, answers=%s, cmap=%s' % (self,answers,newcmap)) # log.debug('%s: in grade_answers, answers=%s, cmap=%s' % (self,answers,newcmap))
...@@ -287,21 +288,21 @@ class LoncapaProblem(object): ...@@ -287,21 +288,21 @@ class LoncapaProblem(object):
try: try:
ifp = self.system.filestore.open(file) # open using I4xSystem OSFS filestore ifp = self.system.filestore.open(file) # open using I4xSystem OSFS filestore
except Exception as err: except Exception as err:
log.error('Error %s in problem xml include: %s' % (err,etree.tostring(inc,pretty_print=True))) log.error('Error %s in problem xml include: %s' % (err, etree.tostring(inc, pretty_print=True)))
log.error('Cannot find file %s in %s' % (file,self.system.filestore)) log.error('Cannot find file %s in %s' % (file, self.system.filestore))
if not self.system.get('DEBUG'): # if debugging, don't fail - just log error if not self.system.get('DEBUG'): # if debugging, don't fail - just log error
raise raise
else: continue else: continue
try: try:
incxml = etree.XML(ifp.read()) # read in and convert to XML incxml = etree.XML(ifp.read()) # read in and convert to XML
except Exception as err: except Exception as err:
log.error('Error %s in problem xml include: %s' % (err,etree.tostring(inc,pretty_print=True))) log.error('Error %s in problem xml include: %s' % (err, etree.tostring(inc, pretty_print=True)))
log.error('Cannot parse XML in %s' % (file)) log.error('Cannot parse XML in %s' % (file))
if not self.system.get('DEBUG'): # if debugging, don't fail - just log error if not self.system.get('DEBUG'): # if debugging, don't fail - just log error
raise raise
else: continue else: continue
parent = inc.getparent() # insert new XML into tree in place of inlcude parent = inc.getparent() # insert new XML into tree in place of inlcude
parent.insert(parent.index(inc),incxml) parent.insert(parent.index(inc), incxml)
parent.remove(inc) parent.remove(inc)
log.debug('Included %s into %s' % (file, self.problem_id)) log.debug('Included %s into %s' % (file, self.problem_id))
...@@ -391,7 +392,7 @@ class LoncapaProblem(object): ...@@ -391,7 +392,7 @@ class LoncapaProblem(object):
Used by get_html. Used by get_html.
''' '''
if problemtree.tag=='script' and problemtree.get('type') and 'javascript' in problemtree.get('type'): if problemtree.tag == 'script' and problemtree.get('type') and 'javascript' in problemtree.get('type'):
# leave javascript intact. # leave javascript intact.
return problemtree return problemtree
...@@ -424,8 +425,8 @@ class LoncapaProblem(object): ...@@ -424,8 +425,8 @@ class LoncapaProblem(object):
'status': status, 'status': status,
'id': problemtree.get('id'), 'id': problemtree.get('id'),
'feedback': {'message': msg, 'feedback': {'message': msg,
'hint' : hint, 'hint': hint,
'hintmode' : hintmode, 'hintmode': hintmode,
} }
}, },
use='capa_input') use='capa_input')
...@@ -466,7 +467,7 @@ class LoncapaProblem(object): ...@@ -466,7 +467,7 @@ class LoncapaProblem(object):
self.responders = {} self.responders = {}
for response in tree.xpath('//' + "|//".join(response_tag_dict)): for response in tree.xpath('//' + "|//".join(response_tag_dict)):
response_id_str = self.problem_id + "_" + str(response_id) response_id_str = self.problem_id + "_" + str(response_id)
response.set('id',response_id_str) # create and save ID for this response response.set('id', response_id_str) # create and save ID for this response
response_id += 1 response_id += 1
answer_id = 1 answer_id = 1
......
...@@ -34,6 +34,7 @@ class DemoSystem(object): ...@@ -34,6 +34,7 @@ class DemoSystem(object):
context_dict.update(context) context_dict.update(context)
return self.lookup.get_template(template_filename).render(**context_dict) return self.lookup.get_template(template_filename).render(**context_dict)
def main(): def main():
parser = argparse.ArgumentParser(description='Check Problem Files') parser = argparse.ArgumentParser(description='Check Problem Files')
parser.add_argument("command", choices=['test', 'show']) # Watch? Render? Open? parser.add_argument("command", choices=['test', 'show']) # Watch? Render? Open?
...@@ -67,6 +68,7 @@ def main(): ...@@ -67,6 +68,7 @@ def main():
# In case we want to do anything else here. # In case we want to do anything else here.
def command_show(problem): def command_show(problem):
"""Display the text for this problem""" """Display the text for this problem"""
print problem.get_html() print problem.get_html()
...@@ -91,6 +93,7 @@ def command_test(problem): ...@@ -91,6 +93,7 @@ def command_test(problem):
finally: finally:
sys.stdout, sys.stderr = old_stdout, old_stderr sys.stdout, sys.stderr = old_stdout, old_stderr
def check_that_blanks_fail(problem): def check_that_blanks_fail(problem):
"""Leaving it blank should never work. Neither should a space.""" """Leaving it blank should never work. Neither should a space."""
blank_answers = dict((answer_id, u"") blank_answers = dict((answer_id, u"")
...@@ -148,6 +151,7 @@ def check_that_suggested_answers_work(problem): ...@@ -148,6 +151,7 @@ def check_that_suggested_answers_work(problem):
log.error("Uncaught error in {0}".format(problem)) log.error("Uncaught error in {0}".format(problem))
log.exception(ex) log.exception(ex)
def log_captured_output(output_stream, stream_name): def log_captured_output(output_stream, stream_name):
output_stream.seek(0) output_stream.seek(0)
output_text = output_stream.read() output_text = output_stream.read()
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
# #
# Used by responsetypes and capa_problem # Used by responsetypes and capa_problem
class CorrectMap(object): class CorrectMap(object):
''' '''
Stores map between answer_id and response evaluation result for each question Stores map between answer_id and response evaluation result for each question
...@@ -18,11 +19,11 @@ class CorrectMap(object): ...@@ -18,11 +19,11 @@ class CorrectMap(object):
Behaves as a dict. Behaves as a dict.
''' '''
def __init__(self,*args,**kwargs): def __init__(self, *args, **kwargs):
self.cmap = dict() # start with empty dict self.cmap = dict() # start with empty dict
self.items = self.cmap.items self.items = self.cmap.items
self.keys = self.cmap.keys self.keys = self.cmap.keys
self.set(*args,**kwargs) self.set(*args, **kwargs)
def __getitem__(self, *args, **kwargs): def __getitem__(self, *args, **kwargs):
return self.cmap.__getitem__(*args, **kwargs) return self.cmap.__getitem__(*args, **kwargs)
...@@ -35,9 +36,9 @@ class CorrectMap(object): ...@@ -35,9 +36,9 @@ class CorrectMap(object):
self.cmap[answer_id] = {'correctness': correctness, self.cmap[answer_id] = {'correctness': correctness,
'npoints': npoints, 'npoints': npoints,
'msg': msg, 'msg': msg,
'hint' : hint, 'hint': hint,
'hintmode' : hintmode, 'hintmode': hintmode,
'queuekey' : queuekey, 'queuekey': queuekey,
} }
def __repr__(self): def __repr__(self):
...@@ -49,67 +50,67 @@ class CorrectMap(object): ...@@ -49,67 +50,67 @@ class CorrectMap(object):
''' '''
return self.cmap return self.cmap
def set_dict(self,correct_map): def set_dict(self, correct_map):
''' '''
set internal dict to provided correct_map dict set internal dict to provided correct_map dict
for graceful migration, if correct_map is a one-level dict, then convert it to the new for graceful migration, if correct_map is a one-level dict, then convert it to the new
dict of dicts format. dict of dicts format.
''' '''
if correct_map and not (type(correct_map[correct_map.keys()[0]])==dict): if correct_map and not (type(correct_map[correct_map.keys()[0]]) == dict):
self.__init__() # empty current dict self.__init__() # empty current dict
for k in correct_map: self.set(k,correct_map[k]) # create new dict entries for k in correct_map: self.set(k, correct_map[k]) # create new dict entries
else: else:
self.cmap = correct_map self.cmap = correct_map
def is_correct(self,answer_id): def is_correct(self, answer_id):
if answer_id in self.cmap: return self.cmap[answer_id]['correctness'] == 'correct' if answer_id in self.cmap: return self.cmap[answer_id]['correctness'] == 'correct'
return None return None
def is_queued(self,answer_id): def is_queued(self, answer_id):
return answer_id in self.cmap and self.cmap[answer_id]['queuekey'] is not None return answer_id in self.cmap and self.cmap[answer_id]['queuekey'] is not None
def is_right_queuekey(self, answer_id, test_key): def is_right_queuekey(self, answer_id, test_key):
return answer_id in self.cmap and self.cmap[answer_id]['queuekey'] == test_key return answer_id in self.cmap and self.cmap[answer_id]['queuekey'] == test_key
def get_npoints(self,answer_id): def get_npoints(self, answer_id):
if self.is_correct(answer_id): if self.is_correct(answer_id):
npoints = self.cmap[answer_id].get('npoints',1) # default to 1 point if correct npoints = self.cmap[answer_id].get('npoints', 1) # default to 1 point if correct
return npoints or 1 return npoints or 1
return 0 # if not correct, return 0 return 0 # if not correct, return 0
def set_property(self,answer_id,property,value): def set_property(self, answer_id, property, value):
if answer_id in self.cmap: self.cmap[answer_id][property] = value if answer_id in self.cmap: self.cmap[answer_id][property] = value
else: self.cmap[answer_id] = {property:value} else: self.cmap[answer_id] = {property: value}
def get_property(self,answer_id,property,default=None): def get_property(self, answer_id, property, default=None):
if answer_id in self.cmap: return self.cmap[answer_id].get(property,default) if answer_id in self.cmap: return self.cmap[answer_id].get(property, default)
return default return default
def get_correctness(self,answer_id): def get_correctness(self, answer_id):
return self.get_property(answer_id,'correctness') return self.get_property(answer_id, 'correctness')
def get_msg(self,answer_id): def get_msg(self, answer_id):
return self.get_property(answer_id,'msg','') return self.get_property(answer_id, 'msg', '')
def get_hint(self,answer_id): def get_hint(self, answer_id):
return self.get_property(answer_id,'hint','') return self.get_property(answer_id, 'hint', '')
def get_hintmode(self,answer_id): def get_hintmode(self, answer_id):
return self.get_property(answer_id,'hintmode',None) return self.get_property(answer_id, 'hintmode', None)
def set_hint_and_mode(self,answer_id,hint,hintmode): def set_hint_and_mode(self, answer_id, hint, hintmode):
''' '''
- hint : (string) HTML text for hint - hint : (string) HTML text for hint
- hintmode : (string) mode for hint display ('always' or 'on_request') - hintmode : (string) mode for hint display ('always' or 'on_request')
''' '''
self.set_property(answer_id,'hint',hint) self.set_property(answer_id, 'hint', hint)
self.set_property(answer_id,'hintmode',hintmode) self.set_property(answer_id, 'hintmode', hintmode)
def update(self,other_cmap): def update(self, other_cmap):
''' '''
Update this CorrectMap with the contents of another CorrectMap Update this CorrectMap with the contents of another CorrectMap
''' '''
if not isinstance(other_cmap,CorrectMap): if not isinstance(other_cmap, CorrectMap):
raise Exception('CorrectMap.update called with invalid argument %s' % other_cmap) raise Exception('CorrectMap.update called with invalid argument %s' % other_cmap)
self.cmap.update(other_cmap.get_dict()) self.cmap.update(other_cmap.get_dict())
......
""" Standard resistor codes. """ Standard resistor codes.
http://en.wikipedia.org/wiki/Electronic_color_code http://en.wikipedia.org/wiki/Electronic_color_code
""" """
E6=[10,15,22,33,47,68] E6 = [10, 15, 22, 33, 47, 68]
E12=[10,12,15,18,22,27,33,39,47,56,68,82] E12 = [10, 12, 15, 18, 22, 27, 33, 39, 47, 56, 68, 82]
E24=[10,12,15,18,22,27,33,39,47,56,68,82,11,13,16,20,24,30,36,43,51,62,75,91] E24 = [10, 12, 15, 18, 22, 27, 33, 39, 47, 56, 68, 82, 11, 13, 16, 20, 24, 30, 36, 43, 51, 62, 75, 91]
E48=[100,121,147,178,215,261,316,383,464,562,681,825,105,127,154,187,226,274,332,402,487,590,715,866,110,133,162,196,237,287,348,422,511,619,750,909,115,140,169,205,249,301,365,442,536,649,787,953] E48 = [100, 121, 147, 178, 215, 261, 316, 383, 464, 562, 681, 825, 105, 127, 154, 187, 226, 274, 332, 402, 487, 590, 715, 866, 110, 133, 162, 196, 237, 287, 348, 422, 511, 619, 750, 909, 115, 140, 169, 205, 249, 301, 365, 442, 536, 649, 787, 953]
E96=[100,121,147,178,215,261,316,383,464,562,681,825,102,124,150,182,221,267,324,392,475,576,698,845,105,127,154,187,226,274,332,402,487,590,715,866,107,130,158,191,232,280,340,412,499,604,732,887,110,133,162,196,237,287,348,422,511,619,750,909,113,137,165,200,243,294,357,432,523,634,768,931,115,140,169,205,249,301,365,442,536,649,787,953,118,143,174,210,255,309,374,453,549,665,806,976] E96 = [100, 121, 147, 178, 215, 261, 316, 383, 464, 562, 681, 825, 102, 124, 150, 182, 221, 267, 324, 392, 475, 576, 698, 845, 105, 127, 154, 187, 226, 274, 332, 402, 487, 590, 715, 866, 107, 130, 158, 191, 232, 280, 340, 412, 499, 604, 732, 887, 110, 133, 162, 196, 237, 287, 348, 422, 511, 619, 750, 909, 113, 137, 165, 200, 243, 294, 357, 432, 523, 634, 768, 931, 115, 140, 169, 205, 249, 301, 365, 442, 536, 649, 787, 953, 118, 143, 174, 210, 255, 309, 374, 453, 549, 665, 806, 976]
E192=[100,121,147,178,215,261,316,383,464,562,681,825,101,123,149,180,218,264,320,388,470,569,690,835,102,124,150,182,221,267,324,392,475,576,698,845,104,126,152,184,223,271,328,397,481,583,706,856,105,127,154,187,226,274,332,402,487,590,715,866,106,129,156,189,229,277,336,407,493,597,723,876,107,130,158,191,232,280,340,412,499,604,732,887,109,132,160,193,234,284,344,417,505,612,741,898,110,133,162,196,237,287,348,422,511,619,750,909,111,135,164,198,240,291,352,427,517,626,759,920,113,137,165,200,243,294,357,432,523,634,768,931,114,138,167,203,246,298,361,437,530,642,777,942,115,140,169,205,249,301,365,442,536,649,787,953,117,142,172,208,252,305,370,448,542,657,796,965,118,143,174,210,255,309,374,453,549,665,806,976,120,145,176,213,258,312,379,459,556,673,816,988] E192 = [100, 121, 147, 178, 215, 261, 316, 383, 464, 562, 681, 825, 101, 123, 149, 180, 218, 264, 320, 388, 470, 569, 690, 835, 102, 124, 150, 182, 221, 267, 324, 392, 475, 576, 698, 845, 104, 126, 152, 184, 223, 271, 328, 397, 481, 583, 706, 856, 105, 127, 154, 187, 226, 274, 332, 402, 487, 590, 715, 866, 106, 129, 156, 189, 229, 277, 336, 407, 493, 597, 723, 876, 107, 130, 158, 191, 232, 280, 340, 412, 499, 604, 732, 887, 109, 132, 160, 193, 234, 284, 344, 417, 505, 612, 741, 898, 110, 133, 162, 196, 237, 287, 348, 422, 511, 619, 750, 909, 111, 135, 164, 198, 240, 291, 352, 427, 517, 626, 759, 920, 113, 137, 165, 200, 243, 294, 357, 432, 523, 634, 768, 931, 114, 138, 167, 203, 246, 298, 361, 437, 530, 642, 777, 942, 115, 140, 169, 205, 249, 301, 365, 442, 536, 649, 787, 953, 117, 142, 172, 208, 252, 305, 370, 448, 542, 657, 796, 965, 118, 143, 174, 210, 255, 309, 374, 453, 549, 665, 806, 976, 120, 145, 176, 213, 258, 312, 379, 459, 556, 673, 816, 988]
...@@ -4,6 +4,7 @@ from calc import evaluator, UndefinedVariable ...@@ -4,6 +4,7 @@ from calc import evaluator, UndefinedVariable
# #
# Utility functions used in CAPA responsetypes # Utility functions used in CAPA responsetypes
def compare_with_tolerance(v1, v2, tol): def compare_with_tolerance(v1, v2, tol):
''' Compare v1 to v2 with maximum tolerance tol ''' Compare v1 to v2 with maximum tolerance tol
tol is relative if it ends in %; otherwise, it is absolute tol is relative if it ends in %; otherwise, it is absolute
...@@ -15,16 +16,17 @@ def compare_with_tolerance(v1, v2, tol): ...@@ -15,16 +16,17 @@ def compare_with_tolerance(v1, v2, tol):
''' '''
relative = tol.endswith('%') relative = tol.endswith('%')
if relative: if relative:
tolerance_rel = evaluator(dict(),dict(),tol[:-1]) * 0.01 tolerance_rel = evaluator(dict(), dict(), tol[:-1]) * 0.01
tolerance = tolerance_rel * max(abs(v1), abs(v2)) tolerance = tolerance_rel * max(abs(v1), abs(v2))
else: else:
tolerance = evaluator(dict(),dict(),tol) tolerance = evaluator(dict(), dict(), tol)
return abs(v1-v2) <= tolerance return abs(v1 - v2) <= tolerance
def contextualize_text(text, context): # private def contextualize_text(text, context): # private
''' Takes a string with variables. E.g. $a+$b. ''' Takes a string with variables. E.g. $a+$b.
Does a substitution of those variables from the context ''' Does a substitution of those variables from the context '''
if not text: return text if not text: return text
for key in sorted(context, lambda x,y:cmp(len(y),len(x))): for key in sorted(context, lambda x, y: cmp(len(y), len(x))):
text=text.replace('$'+key, str(context[key])) text = text.replace('$' + key, str(context[key]))
return text return text
...@@ -13,4 +13,3 @@ ...@@ -13,4 +13,3 @@
# limitations under the License. # limitations under the License.
lookup = None lookup = None
...@@ -22,6 +22,7 @@ from django.http import HttpResponse ...@@ -22,6 +22,7 @@ from django.http import HttpResponse
from . import middleware from . import middleware
from django.conf import settings from django.conf import settings
def render_to_string(template_name, dictionary, context=None, namespace='main'): def render_to_string(template_name, dictionary, context=None, namespace='main'):
context_instance = Context(dictionary) context_instance = Context(dictionary)
# add dictionary to context_instance # add dictionary to context_instance
...@@ -43,6 +44,7 @@ def render_to_string(template_name, dictionary, context=None, namespace='main'): ...@@ -43,6 +44,7 @@ def render_to_string(template_name, dictionary, context=None, namespace='main'):
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)
def render_to_response(template_name, dictionary, context_instance=None, namespace='main', **kwargs): def render_to_response(template_name, dictionary, context_instance=None, namespace='main', **kwargs):
""" """
Returns a HttpResponse whose content is filled with the result of calling Returns a HttpResponse whose content is filled with the result of calling
......
...@@ -16,6 +16,7 @@ frac() and __str__(). ...@@ -16,6 +16,7 @@ frac() and __str__().
from collections import namedtuple from collections import namedtuple
import numbers import numbers
class Progress(object): class Progress(object):
'''Represents a progress of a/b (a out of b done) '''Represents a progress of a/b (a out of b done)
...@@ -66,7 +67,6 @@ class Progress(object): ...@@ -66,7 +67,6 @@ class Progress(object):
''' '''
return self.frac()[0] > 0 return self.frac()[0] > 0
def inprogress(self): def inprogress(self):
''' Returns True if fractional progress is strictly between 0 and 1. ''' Returns True if fractional progress is strictly between 0 and 1.
...@@ -83,8 +83,7 @@ class Progress(object): ...@@ -83,8 +83,7 @@ class Progress(object):
checking is done at construction time. checking is done at construction time.
''' '''
(a, b) = self.frac() (a, b) = self.frac()
return a==b return a == b
def ternary_str(self): def ternary_str(self):
''' Return a string version of this progress: either ''' Return a string version of this progress: either
...@@ -112,7 +111,6 @@ class Progress(object): ...@@ -112,7 +111,6 @@ class Progress(object):
''' The opposite of equal''' ''' The opposite of equal'''
return not self.__eq__(other) return not self.__eq__(other)
def __str__(self): def __str__(self):
''' Return a string representation of this string. ''' Return a string representation of this string.
...@@ -147,7 +145,6 @@ class Progress(object): ...@@ -147,7 +145,6 @@ class Progress(object):
return "NA" return "NA"
return progress.ternary_str() return progress.ternary_str()
@staticmethod @staticmethod
def to_js_detail_str(progress): def to_js_detail_str(progress):
''' '''
......
...@@ -198,8 +198,8 @@ class CapaModule(XModule): ...@@ -198,8 +198,8 @@ class CapaModule(XModule):
if self.system.DEBUG: if self.system.DEBUG:
log.exception(err) log.exception(err)
msg = '[courseware.capa.capa_module] <font size="+1" color="red">Failed to generate HTML for problem %s</font>' % (self.location.url()) msg = '[courseware.capa.capa_module] <font size="+1" color="red">Failed to generate HTML for problem %s</font>' % (self.location.url())
msg += '<p>Error:</p><p><pre>%s</pre></p>' % str(err).replace('<','&lt;') msg += '<p>Error:</p><p><pre>%s</pre></p>' % str(err).replace('<', '&lt;')
msg += '<p><pre>%s</pre></p>' % traceback.format_exc().replace('<','&lt;') msg += '<p><pre>%s</pre></p>' % traceback.format_exc().replace('<', '&lt;')
html = msg html = msg
else: else:
raise raise
...@@ -430,7 +430,7 @@ class CapaModule(XModule): ...@@ -430,7 +430,7 @@ class CapaModule(XModule):
if self.system.DEBUG: if self.system.DEBUG:
msg = "Error checking problem: " + str(err) msg = "Error checking problem: " + str(err)
msg += '\nTraceback:\n' + traceback.format_exc() msg += '\nTraceback:\n' + traceback.format_exc()
return {'success':msg} return {'success': msg}
traceback.print_exc() traceback.print_exc()
raise Exception("error in capa_module") raise Exception("error in capa_module")
......
...@@ -9,7 +9,6 @@ from fs.errors import ResourceNotFoundError ...@@ -9,7 +9,6 @@ from fs.errors import ResourceNotFoundError
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class CourseDescriptor(SequenceDescriptor): class CourseDescriptor(SequenceDescriptor):
module_class = SequenceModule module_class = SequenceModule
...@@ -19,10 +18,10 @@ class CourseDescriptor(SequenceDescriptor): ...@@ -19,10 +18,10 @@ class CourseDescriptor(SequenceDescriptor):
try: try:
self.start = time.strptime(self.metadata["start"], "%Y-%m-%dT%H:%M") self.start = time.strptime(self.metadata["start"], "%Y-%m-%dT%H:%M")
except KeyError: except KeyError:
self.start = time.gmtime(0) #The epoch self.start = time.gmtime(0) # The epoch
log.critical("Course loaded without a start date. " + str(self.id)) log.critical("Course loaded without a start date. " + str(self.id))
except ValueError, e: except ValueError, e:
self.start = time.gmtime(0) #The epoch self.start = time.gmtime(0) # The epoch
log.critical("Course loaded with a bad start date. " + str(self.id) + " '" + str(e) + "'") log.critical("Course loaded with a bad start date. " + str(self.id) + " '" + str(e) + "'")
def has_started(self): def has_started(self):
......
...@@ -57,11 +57,11 @@ def grader_from_conf(conf): ...@@ -57,11 +57,11 @@ def grader_from_conf(conf):
if 'min_count' in subgraderconf: if 'min_count' in subgraderconf:
#This is an AssignmentFormatGrader #This is an AssignmentFormatGrader
subgrader = AssignmentFormatGrader(**subgraderconf) subgrader = AssignmentFormatGrader(**subgraderconf)
subgraders.append( (subgrader, subgrader.category, weight) ) subgraders.append((subgrader, subgrader.category, weight))
elif 'name' in subgraderconf: elif 'name' in subgraderconf:
#This is an SingleSectionGrader #This is an SingleSectionGrader
subgrader = SingleSectionGrader(**subgraderconf) subgrader = SingleSectionGrader(**subgraderconf)
subgraders.append( (subgrader, subgrader.category, weight) ) subgraders.append((subgrader, subgrader.category, weight))
else: else:
raise ValueError("Configuration has no appropriate grader class.") raise ValueError("Configuration has no appropriate grader class.")
...@@ -70,7 +70,7 @@ def grader_from_conf(conf): ...@@ -70,7 +70,7 @@ def grader_from_conf(conf):
log.critical(errorString) log.critical(errorString)
raise ValueError(errorString) raise ValueError(errorString)
return WeightedSubsectionsGrader( subgraders ) return WeightedSubsectionsGrader(subgraders)
class CourseGrader(object): class CourseGrader(object):
...@@ -120,6 +120,7 @@ class CourseGrader(object): ...@@ -120,6 +120,7 @@ class CourseGrader(object):
def grade(self, grade_sheet): def grade(self, grade_sheet):
raise NotImplementedError raise NotImplementedError
class WeightedSubsectionsGrader(CourseGrader): class WeightedSubsectionsGrader(CourseGrader):
""" """
This grader takes a list of tuples containing (grader, category_name, weight) and computes This grader takes a list of tuples containing (grader, category_name, weight) and computes
...@@ -149,11 +150,11 @@ class WeightedSubsectionsGrader(CourseGrader): ...@@ -149,11 +150,11 @@ class WeightedSubsectionsGrader(CourseGrader):
total_percent += weightedPercent total_percent += weightedPercent
section_breakdown += subgrade_result['section_breakdown'] section_breakdown += subgrade_result['section_breakdown']
grade_breakdown.append( {'percent' : weightedPercent, 'detail' : section_detail, 'category' : category} ) grade_breakdown.append({'percent': weightedPercent, 'detail': section_detail, 'category': category})
return {'percent' : total_percent, return {'percent': total_percent,
'section_breakdown' : section_breakdown, 'section_breakdown': section_breakdown,
'grade_breakdown' : grade_breakdown} 'grade_breakdown': grade_breakdown}
class SingleSectionGrader(CourseGrader): class SingleSectionGrader(CourseGrader):
...@@ -163,7 +164,7 @@ class SingleSectionGrader(CourseGrader): ...@@ -163,7 +164,7 @@ class SingleSectionGrader(CourseGrader):
If the name is not appropriate for the short short_label or category, they each may If the name is not appropriate for the short short_label or category, they each may
be specified individually. be specified individually.
""" """
def __init__(self, type, name, short_label = None, category = None): def __init__(self, type, name, short_label=None, category=None):
self.type = type self.type = type
self.name = name self.name = name
self.short_label = short_label or name self.short_label = short_label or name
...@@ -179,22 +180,23 @@ class SingleSectionGrader(CourseGrader): ...@@ -179,22 +180,23 @@ class SingleSectionGrader(CourseGrader):
if foundScore: if foundScore:
percent = foundScore.earned / float(foundScore.possible) percent = foundScore.earned / float(foundScore.possible)
detail = "{name} - {percent:.0%} ({earned:.3n}/{possible:.3n})".format( name = self.name, detail = "{name} - {percent:.0%} ({earned:.3n}/{possible:.3n})".format(name=self.name,
percent = percent, percent=percent,
earned = float(foundScore.earned), earned=float(foundScore.earned),
possible = float(foundScore.possible)) possible=float(foundScore.possible))
else: else:
percent = 0.0 percent = 0.0
detail = "{name} - 0% (?/?)".format(name = self.name) detail = "{name} - 0% (?/?)".format(name=self.name)
breakdown = [{'percent': percent, 'label': self.short_label, 'detail': detail, 'category': self.category, 'prominent': True}] breakdown = [{'percent': percent, 'label': self.short_label, 'detail': detail, 'category': self.category, 'prominent': True}]
return {'percent' : percent, return {'percent': percent,
'section_breakdown' : breakdown, 'section_breakdown': breakdown,
#No grade_breakdown here #No grade_breakdown here
} }
class AssignmentFormatGrader(CourseGrader): class AssignmentFormatGrader(CourseGrader):
""" """
Grades all sections matching the format 'type' with an equal weight. A specified Grades all sections matching the format 'type' with an equal weight. A specified
...@@ -216,7 +218,7 @@ class AssignmentFormatGrader(CourseGrader): ...@@ -216,7 +218,7 @@ class AssignmentFormatGrader(CourseGrader):
"HW". "HW".
""" """
def __init__(self, type, min_count, drop_count, category = None, section_type = None, short_label = None): def __init__(self, type, min_count, drop_count, category=None, section_type=None, short_label=None):
self.type = type self.type = type
self.min_count = min_count self.min_count = min_count
self.drop_count = drop_count self.drop_count = drop_count
...@@ -227,7 +229,7 @@ class AssignmentFormatGrader(CourseGrader): ...@@ -227,7 +229,7 @@ class AssignmentFormatGrader(CourseGrader):
def grade(self, grade_sheet): def grade(self, grade_sheet):
def totalWithDrops(breakdown, drop_count): def totalWithDrops(breakdown, drop_count):
#create an array of tuples with (index, mark), sorted by mark['percent'] descending #create an array of tuples with (index, mark), sorted by mark['percent'] descending
sorted_breakdown = sorted( enumerate(breakdown), key=lambda x: -x[1]['percent'] ) sorted_breakdown = sorted(enumerate(breakdown), key=lambda x: -x[1]['percent'])
# A list of the indices of the dropped scores # A list of the indices of the dropped scores
dropped_indices = [] dropped_indices = []
if drop_count > 0: if drop_count > 0:
...@@ -245,35 +247,33 @@ class AssignmentFormatGrader(CourseGrader): ...@@ -245,35 +247,33 @@ class AssignmentFormatGrader(CourseGrader):
#Figure the homework scores #Figure the homework scores
scores = grade_sheet.get(self.type, []) scores = grade_sheet.get(self.type, [])
breakdown = [] breakdown = []
for i in range( max(self.min_count, len(scores)) ): for i in range(max(self.min_count, len(scores))):
if i < len(scores): if i < len(scores):
percentage = scores[i].earned / float(scores[i].possible) percentage = scores[i].earned / float(scores[i].possible)
summary = "{section_type} {index} - {name} - {percent:.0%} ({earned:.3n}/{possible:.3n})".format(index = i+1, summary = "{section_type} {index} - {name} - {percent:.0%} ({earned:.3n}/{possible:.3n})".format(index=i + 1,
section_type = self.section_type, section_type=self.section_type,
name = scores[i].section, name=scores[i].section,
percent = percentage, percent=percentage,
earned = float(scores[i].earned), earned=float(scores[i].earned),
possible = float(scores[i].possible) ) possible=float(scores[i].possible))
else: else:
percentage = 0 percentage = 0
summary = "{section_type} {index} Unreleased - 0% (?/?)".format(index = i+1, section_type = self.section_type) summary = "{section_type} {index} Unreleased - 0% (?/?)".format(index=i + 1, section_type=self.section_type)
short_label = "{short_label} {index:02d}".format(index = i+1, short_label = self.short_label) short_label = "{short_label} {index:02d}".format(index=i + 1, short_label=self.short_label)
breakdown.append( {'percent': percentage, 'label': short_label, 'detail': summary, 'category': self.category} ) breakdown.append({'percent': percentage, 'label': short_label, 'detail': summary, 'category': self.category})
total_percent, dropped_indices = totalWithDrops(breakdown, self.drop_count) total_percent, dropped_indices = totalWithDrops(breakdown, self.drop_count)
for dropped_index in dropped_indices: for dropped_index in dropped_indices:
breakdown[dropped_index]['mark'] = {'detail': "The lowest {drop_count} {section_type} scores are dropped.".format(drop_count = self.drop_count, section_type=self.section_type) } breakdown[dropped_index]['mark'] = {'detail': "The lowest {drop_count} {section_type} scores are dropped.".format(drop_count=self.drop_count, section_type=self.section_type)}
total_detail = "{section_type} Average = {percent:.0%}".format(percent = total_percent, section_type = self.section_type)
total_label = "{short_label} Avg".format(short_label = self.short_label)
breakdown.append( {'percent': total_percent, 'label': total_label, 'detail': total_detail, 'category': self.category, 'prominent': True} )
total_detail = "{section_type} Average = {percent:.0%}".format(percent=total_percent, section_type=self.section_type)
total_label = "{short_label} Avg".format(short_label=self.short_label)
breakdown.append({'percent': total_percent, 'label': total_label, 'detail': total_detail, 'category': self.category, 'prominent': True})
return {'percent' : total_percent, return {'percent': total_percent,
'section_breakdown' : breakdown, 'section_breakdown': breakdown,
#No grade_breakdown here #No grade_breakdown here
} }
...@@ -81,7 +81,7 @@ class Location(_LocationBase): ...@@ -81,7 +81,7 @@ class Location(_LocationBase):
def check_list(list_): def check_list(list_):
for val in list_: for val in list_:
if val is not None and INVALID_CHARS.search(val) is not None: if val is not None and INVALID_CHARS.search(val) is not None:
log.debug('invalid characters val="%s", list_="%s"' % (val,list_)) log.debug('invalid characters val="%s", list_="%s"' % (val, list_))
raise InvalidLocationError(location) raise InvalidLocationError(location)
if isinstance(location, basestring): if isinstance(location, basestring):
......
...@@ -10,5 +10,6 @@ class ItemNotFoundError(Exception): ...@@ -10,5 +10,6 @@ class ItemNotFoundError(Exception):
class InsufficientSpecificationError(Exception): class InsufficientSpecificationError(Exception):
pass pass
class InvalidLocationError(Exception): class InvalidLocationError(Exception):
pass pass
...@@ -56,6 +56,7 @@ def location_to_query(location): ...@@ -56,6 +56,7 @@ def location_to_query(location):
return query return query
class MongoModuleStore(ModuleStore): class MongoModuleStore(ModuleStore):
""" """
A Mongodb backed ModuleStore A Mongodb backed ModuleStore
......
...@@ -51,6 +51,7 @@ def test_invalid_locations(): ...@@ -51,6 +51,7 @@ def test_invalid_locations():
assert_raises(InvalidLocationError, Location, None) assert_raises(InvalidLocationError, Location, None)
assert_raises(InvalidLocationError, Location, "tag://org/course/category/name with spaces/revision") assert_raises(InvalidLocationError, Location, "tag://org/course/category/name with spaces/revision")
def test_equality(): def test_equality():
assert_equals( assert_equals(
Location('tag', 'org', 'course', 'category', 'name'), Location('tag', 'org', 'course', 'category', 'name'),
......
...@@ -53,7 +53,7 @@ class XMLModuleStore(ModuleStore): ...@@ -53,7 +53,7 @@ class XMLModuleStore(ModuleStore):
class_ = getattr(import_module(module_path), class_name) class_ = getattr(import_module(module_path), class_name)
self.default_class = class_ self.default_class = class_
log.debug('XMLModuleStore: eager=%s, data_dir = %s' % (eager,self.data_dir)) log.debug('XMLModuleStore: eager=%s, data_dir = %s' % (eager, self.data_dir))
log.debug('default_class = %s' % self.default_class) log.debug('default_class = %s' % self.default_class)
for course_dir in os.listdir(self.data_dir): for course_dir in os.listdir(self.data_dir):
......
...@@ -16,6 +16,7 @@ frac() and __str__(). ...@@ -16,6 +16,7 @@ frac() and __str__().
from collections import namedtuple from collections import namedtuple
import numbers import numbers
class Progress(object): class Progress(object):
'''Represents a progress of a/b (a out of b done) '''Represents a progress of a/b (a out of b done)
...@@ -66,7 +67,6 @@ class Progress(object): ...@@ -66,7 +67,6 @@ class Progress(object):
''' '''
return self.frac()[0] > 0 return self.frac()[0] > 0
def inprogress(self): def inprogress(self):
''' Returns True if fractional progress is strictly between 0 and 1. ''' Returns True if fractional progress is strictly between 0 and 1.
...@@ -83,8 +83,7 @@ class Progress(object): ...@@ -83,8 +83,7 @@ class Progress(object):
checking is done at construction time. checking is done at construction time.
''' '''
(a, b) = self.frac() (a, b) = self.frac()
return a==b return a == b
def ternary_str(self): def ternary_str(self):
''' Return a string version of this progress: either ''' Return a string version of this progress: either
...@@ -112,7 +111,6 @@ class Progress(object): ...@@ -112,7 +111,6 @@ class Progress(object):
''' The opposite of equal''' ''' The opposite of equal'''
return not self.__eq__(other) return not self.__eq__(other)
def __str__(self): def __str__(self):
''' Return a string representation of this string. ''' Return a string representation of this string.
...@@ -147,7 +145,6 @@ class Progress(object): ...@@ -147,7 +145,6 @@ class Progress(object):
return "NA" return "NA"
return progress.ternary_str() return progress.ternary_str()
@staticmethod @staticmethod
def to_js_detail_str(progress): def to_js_detail_str(progress):
''' '''
......
...@@ -2,9 +2,11 @@ import json ...@@ -2,9 +2,11 @@ import json
from x_module import XModule, XModuleDescriptor from x_module import XModule, XModuleDescriptor
class ModuleDescriptor(XModuleDescriptor): class ModuleDescriptor(XModuleDescriptor):
pass pass
class Module(XModule): 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)
...@@ -53,9 +53,9 @@ class SequenceModule(XModule): ...@@ -53,9 +53,9 @@ class SequenceModule(XModule):
def handle_ajax(self, dispatch, get): # TODO: bounds checking def handle_ajax(self, dispatch, get): # TODO: bounds checking
''' get = request.POST instance ''' ''' get = request.POST instance '''
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 self.system.exception404 raise self.system.exception404
def render(self): def render(self):
......
...@@ -36,7 +36,7 @@ class VideoModule(XModule): ...@@ -36,7 +36,7 @@ class VideoModule(XModule):
if dispatch == 'goto_position': if dispatch == 'goto_position':
self.position = int(float(get['position'])) self.position = int(float(get['position']))
log.info(u"NEW POSITION {0}".format(self.position)) log.info(u"NEW POSITION {0}".format(self.position))
return json.dumps({'success':True}) return json.dumps({'success': True})
raise Http404() raise Http404()
def get_progress(self): def get_progress(self):
......
...@@ -7,6 +7,7 @@ from functools import partial ...@@ -7,6 +7,7 @@ from functools import partial
log = logging.getLogger('mitx.' + __name__) log = logging.getLogger('mitx.' + __name__)
def dummy_track(event_type, event): def dummy_track(event_type, event):
pass pass
......
from django.utils.simplejson import dumps from django.utils.simplejson import dumps
from django.core.management.base import BaseCommand, CommandError from django.core.management.base import BaseCommand, CommandError
from certificates.models import GeneratedCertificate from certificates.models import GeneratedCertificate
class Command(BaseCommand): class Command(BaseCommand):
help = """ help = """
...@@ -14,7 +16,7 @@ class Command(BaseCommand): ...@@ -14,7 +16,7 @@ class Command(BaseCommand):
def handle(self, *args, **options): def handle(self, *args, **options):
users = GeneratedCertificate.objects.filter( users = GeneratedCertificate.objects.filter(
download_url = None ) download_url=None)
user_output = [{'user_id':user.user_id, 'name':user.name} user_output = [{'user_id':user.user_id, 'name':user.name}
for user in users] for user in users]
self.stdout.write(dumps(user_output) + "\n") self.stdout.write(dumps(user_output) + "\n")
...@@ -4,6 +4,7 @@ from south.db import db ...@@ -4,6 +4,7 @@ from south.db import db
from south.v2 import SchemaMigration from south.v2 import SchemaMigration
from django.db import models from django.db import models
class Migration(SchemaMigration): class Migration(SchemaMigration):
def forwards(self, orm): def forwards(self, orm):
...@@ -17,7 +18,6 @@ class Migration(SchemaMigration): ...@@ -17,7 +18,6 @@ class Migration(SchemaMigration):
# Adding field 'GeneratedCertificate.grade' # Adding field 'GeneratedCertificate.grade'
db.add_column('certificates_generatedcertificate', 'grade', self.gf('django.db.models.fields.CharField')(max_length=5, null=True), keep_default=False) db.add_column('certificates_generatedcertificate', 'grade', self.gf('django.db.models.fields.CharField')(max_length=5, null=True), keep_default=False)
def backwards(self, orm): def backwards(self, orm):
# Deleting field 'GeneratedCertificate.graded_certificate_id' # Deleting field 'GeneratedCertificate.graded_certificate_id'
...@@ -29,7 +29,6 @@ class Migration(SchemaMigration): ...@@ -29,7 +29,6 @@ class Migration(SchemaMigration):
# Deleting field 'GeneratedCertificate.grade' # Deleting field 'GeneratedCertificate.grade'
db.delete_column('certificates_generatedcertificate', 'grade') db.delete_column('certificates_generatedcertificate', 'grade')
models = { models = {
'auth.group': { 'auth.group': {
'Meta': {'object_name': 'Group'}, 'Meta': {'object_name': 'Group'},
......
...@@ -4,6 +4,7 @@ from south.db import db ...@@ -4,6 +4,7 @@ from south.db import db
from south.v2 import SchemaMigration from south.v2 import SchemaMigration
from django.db import models from django.db import models
class Migration(SchemaMigration): class Migration(SchemaMigration):
def forwards(self, orm): def forwards(self, orm):
...@@ -11,13 +12,11 @@ class Migration(SchemaMigration): ...@@ -11,13 +12,11 @@ class Migration(SchemaMigration):
# Adding field 'GeneratedCertificate.name' # Adding field 'GeneratedCertificate.name'
db.add_column('certificates_generatedcertificate', 'name', self.gf('django.db.models.fields.CharField')(default='', max_length=255, blank=True), keep_default=False) db.add_column('certificates_generatedcertificate', 'name', self.gf('django.db.models.fields.CharField')(default='', max_length=255, blank=True), keep_default=False)
def backwards(self, orm): def backwards(self, orm):
# Deleting field 'GeneratedCertificate.name' # Deleting field 'GeneratedCertificate.name'
db.delete_column('certificates_generatedcertificate', 'name') db.delete_column('certificates_generatedcertificate', 'name')
models = { models = {
'auth.group': { 'auth.group': {
'Meta': {'object_name': 'Group'}, 'Meta': {'object_name': 'Group'},
......
...@@ -4,6 +4,7 @@ from south.db import db ...@@ -4,6 +4,7 @@ from south.db import db
from south.v2 import SchemaMigration from south.v2 import SchemaMigration
from django.db import models from django.db import models
class Migration(SchemaMigration): class Migration(SchemaMigration):
def forwards(self, orm): def forwards(self, orm):
...@@ -11,13 +12,11 @@ class Migration(SchemaMigration): ...@@ -11,13 +12,11 @@ class Migration(SchemaMigration):
# Changing field 'GeneratedCertificate.certificate_id' # Changing field 'GeneratedCertificate.certificate_id'
db.alter_column('certificates_generatedcertificate', 'certificate_id', self.gf('django.db.models.fields.CharField')(max_length=32, null=True)) db.alter_column('certificates_generatedcertificate', 'certificate_id', self.gf('django.db.models.fields.CharField')(max_length=32, null=True))
def backwards(self, orm): def backwards(self, orm):
# Changing field 'GeneratedCertificate.certificate_id' # Changing field 'GeneratedCertificate.certificate_id'
db.alter_column('certificates_generatedcertificate', 'certificate_id', self.gf('django.db.models.fields.CharField')(default=None, max_length=32)) db.alter_column('certificates_generatedcertificate', 'certificate_id', self.gf('django.db.models.fields.CharField')(default=None, max_length=32))
models = { models = {
'auth.group': { 'auth.group': {
'Meta': {'object_name': 'Group'}, 'Meta': {'object_name': 'Group'},
......
...@@ -4,6 +4,7 @@ from south.db import db ...@@ -4,6 +4,7 @@ from south.db import db
from south.v2 import SchemaMigration from south.v2 import SchemaMigration
from django.db import models from django.db import models
class Migration(SchemaMigration): class Migration(SchemaMigration):
def forwards(self, orm): def forwards(self, orm):
...@@ -23,13 +24,11 @@ class Migration(SchemaMigration): ...@@ -23,13 +24,11 @@ class Migration(SchemaMigration):
)) ))
db.send_create_signal('certificates', ['RevokedCertificate']) db.send_create_signal('certificates', ['RevokedCertificate'])
def backwards(self, orm): def backwards(self, orm):
# Deleting model 'RevokedCertificate' # Deleting model 'RevokedCertificate'
db.delete_table('certificates_revokedcertificate') db.delete_table('certificates_revokedcertificate')
models = { models = {
'auth.group': { 'auth.group': {
'Meta': {'object_name': 'Group'}, 'Meta': {'object_name': 'Group'},
......
...@@ -26,6 +26,7 @@ needs to be set to true. ...@@ -26,6 +26,7 @@ needs to be set to true.
''' '''
class GeneratedCertificate(models.Model): class GeneratedCertificate(models.Model):
user = models.ForeignKey(User, db_index=True) user = models.ForeignKey(User, db_index=True)
# This is the name at the time of request # This is the name at the time of request
...@@ -43,6 +44,7 @@ class GeneratedCertificate(models.Model): ...@@ -43,6 +44,7 @@ class GeneratedCertificate(models.Model):
# The student must have a grade and request a certificate for enabled to be True # The student must have a grade and request a certificate for enabled to be True
enabled = models.BooleanField(default=False) enabled = models.BooleanField(default=False)
class RevokedCertificate(models.Model): class RevokedCertificate(models.Model):
""" """
This model is for when a GeneratedCertificate must be regenerated. This model This model is for when a GeneratedCertificate must be regenerated. This model
...@@ -82,14 +84,14 @@ def revoke_certificate(certificate, explanation): ...@@ -82,14 +84,14 @@ def revoke_certificate(certificate, explanation):
Once this method has been called, it is safe to delete the certificate, or modify the Once this method has been called, it is safe to delete the certificate, or modify the
certificate's name or grade until it has been generated again. certificate's name or grade until it has been generated again.
""" """
revoked = RevokedCertificate( user = certificate.user, revoked = RevokedCertificate(user=certificate.user,
name = certificate.name, name=certificate.name,
certificate_id = certificate.certificate_id, certificate_id=certificate.certificate_id,
graded_certificate_id = certificate.graded_certificate_id, graded_certificate_id=certificate.graded_certificate_id,
download_url = certificate.download_url, download_url=certificate.download_url,
graded_download_url = certificate.graded_download_url, graded_download_url=certificate.graded_download_url,
grade = certificate.grade, grade=certificate.grade,
enabled = certificate.enabled) enabled=certificate.enabled)
revoked.explanation = explanation revoked.explanation = explanation
...@@ -102,8 +104,6 @@ def revoke_certificate(certificate, explanation): ...@@ -102,8 +104,6 @@ def revoke_certificate(certificate, explanation):
revoked.save() revoked.save()
def certificate_state_for_student(student, grade): def certificate_state_for_student(student, grade):
''' '''
This returns a dictionary with a key for state, and other information. The state is one of the This returns a dictionary with a key for state, and other information. The state is one of the
...@@ -121,24 +121,24 @@ def certificate_state_for_student(student, grade): ...@@ -121,24 +121,24 @@ def certificate_state_for_student(student, grade):
if grade: if grade:
#TODO: Remove the following after debugging #TODO: Remove the following after debugging
if settings.DEBUG_SURVEY: if settings.DEBUG_SURVEY:
return {'state' : 'requestable' } return {'state': 'requestable'}
try: try:
generated_certificate = GeneratedCertificate.objects.get(user = student) generated_certificate = GeneratedCertificate.objects.get(user=student)
if generated_certificate.enabled: if generated_certificate.enabled:
if generated_certificate.download_url: if generated_certificate.download_url:
return {'state' : 'downloadable', return {'state': 'downloadable',
'download_url' : generated_certificate.download_url, 'download_url': generated_certificate.download_url,
'graded_download_url' : generated_certificate.graded_download_url} 'graded_download_url': generated_certificate.graded_download_url}
else: else:
return {'state' : 'generating'} return {'state': 'generating'}
else: else:
# If enabled=False, it may have been pre-generated but not yet requested # If enabled=False, it may have been pre-generated but not yet requested
# Our output will be the same as if the GeneratedCertificate did not exist # Our output will be the same as if the GeneratedCertificate did not exist
pass pass
except GeneratedCertificate.DoesNotExist: except GeneratedCertificate.DoesNotExist:
pass pass
return {'state' : 'requestable'} return {'state': 'requestable'}
else: else:
# No grade, no certificate. No exceptions # No grade, no certificate. No exceptions
return {'state' : 'unavailable'} return {'state': 'unavailable'}
...@@ -18,6 +18,7 @@ from student.models import UserProfile ...@@ -18,6 +18,7 @@ from student.models import UserProfile
log = logging.getLogger("mitx.certificates") log = logging.getLogger("mitx.certificates")
@login_required @login_required
def certificate_request(request): def certificate_request(request):
''' Attempt to send a certificate. ''' ''' Attempt to send a certificate. '''
...@@ -31,8 +32,8 @@ def certificate_request(request): ...@@ -31,8 +32,8 @@ def certificate_request(request):
error = '' error = ''
def return_error(error): def return_error(error):
return HttpResponse(json.dumps({'success':False, return HttpResponse(json.dumps({'success': False,
'error': error })) 'error': error}))
if honor_code_verify != 'true': if honor_code_verify != 'true':
error += 'Please verify that you have followed the honor code to receive a certificate. ' error += 'Please verify that you have followed the honor code to receive a certificate. '
...@@ -48,7 +49,7 @@ def certificate_request(request): ...@@ -48,7 +49,7 @@ def certificate_request(request):
survey_response = record_exit_survey(request, internal_request=True) survey_response = record_exit_survey(request, internal_request=True)
if not survey_response['success']: if not survey_response['success']:
return return_error( survey_response['error'] ) return return_error(survey_response['error'])
grade = None grade = None
student_gradesheet = grades.grade_sheet(request.user) student_gradesheet = grades.grade_sheet(request.user)
...@@ -59,7 +60,7 @@ def certificate_request(request): ...@@ -59,7 +60,7 @@ def certificate_request(request):
generate_certificate(request.user, grade) generate_certificate(request.user, grade)
return HttpResponse(json.dumps({'success':True})) return HttpResponse(json.dumps({'success': True}))
else: else:
#This is not a POST, we should render the page with the form #This is not a POST, we should render the page with the form
...@@ -79,17 +80,14 @@ def certificate_request(request): ...@@ -79,17 +80,14 @@ def certificate_request(request):
if not took_survey: if not took_survey:
survey_list = exit_survey_list_for_student(request.user) survey_list = exit_survey_list_for_student(request.user)
context = {'certificate_state': certificate_state,
context = {'certificate_state' : certificate_state, 'took_survey': took_survey,
'took_survey' : took_survey, 'survey_list': survey_list,
'survey_list' : survey_list, 'name': user_info.name}
'name' : user_info.name }
return render_to_response('cert_request.html', context) return render_to_response('cert_request.html', context)
# This method should only be called if the user has a grade and has requested a certificate # This method should only be called if the user has a grade and has requested a certificate
def generate_certificate(user, grade): def generate_certificate(user, grade):
# Make sure to see the comments in models.GeneratedCertificate to read about the valid # Make sure to see the comments in models.GeneratedCertificate to read about the valid
...@@ -98,9 +96,9 @@ def generate_certificate(user, grade): ...@@ -98,9 +96,9 @@ def generate_certificate(user, grade):
generated_certificate = None generated_certificate = None
try: try:
generated_certificate = GeneratedCertificate.objects.get(user = user) generated_certificate = GeneratedCertificate.objects.get(user=user)
except GeneratedCertificate.DoesNotExist: except GeneratedCertificate.DoesNotExist:
generated_certificate = GeneratedCertificate(user = user) generated_certificate = GeneratedCertificate(user=user)
generated_certificate.enabled = True generated_certificate.enabled = True
if generated_certificate.graded_download_url and (generated_certificate.grade != grade): if generated_certificate.graded_download_url and (generated_certificate.grade != grade):
...@@ -115,7 +113,7 @@ def generate_certificate(user, grade): ...@@ -115,7 +113,7 @@ def generate_certificate(user, grade):
userid=user.id)) userid=user.id))
revoke_certificate(generated_certificate, "The grade on this certificate may be inaccurate.") revoke_certificate(generated_certificate, "The grade on this certificate may be inaccurate.")
user_name = UserProfile.objects.get(user = user).name user_name = UserProfile.objects.get(user=user).name
if generated_certificate.download_url and (generated_certificate.name != user_name): if generated_certificate.download_url and (generated_certificate.name != user_name):
log.critical(u"A Certificate has been pre-generated with the name of " log.critical(u"A Certificate has been pre-generated with the name of "
"{gen_name} but current name is {user_name} (user id is " "{gen_name} but current name is {user_name} (user id is "
...@@ -128,7 +126,6 @@ def generate_certificate(user, grade): ...@@ -128,7 +126,6 @@ def generate_certificate(user, grade):
userid=user.id)) userid=user.id))
revoke_certificate(generated_certificate, "The name on this certificate may be inaccurate.") revoke_certificate(generated_certificate, "The name on this certificate may be inaccurate.")
generated_certificate.grade = grade generated_certificate.grade = grade
generated_certificate.name = user_name generated_certificate.name = user_name
generated_certificate.save() generated_certificate.save()
...@@ -139,11 +136,11 @@ def generate_certificate(user, grade): ...@@ -139,11 +136,11 @@ def generate_certificate(user, grade):
# TODO: If the certificate was pre-generated, send the email that it is ready to download # TODO: If the certificate was pre-generated, send the email that it is ready to download
if certificate_state_for_student(user, grade)['state'] == "downloadable": if certificate_state_for_student(user, grade)['state'] == "downloadable":
subject = render_to_string('emails/certificate_ready_subject.txt',{}) subject = render_to_string('emails/certificate_ready_subject.txt', {})
subject = ''.join(subject.splitlines()) subject = ''.join(subject.splitlines())
message = render_to_string('emails/certificate_ready.txt',{}) message = render_to_string('emails/certificate_ready.txt', {})
res=send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, [user.email,]) res = send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, [user.email, ])
else: else:
log.warning("Asked to generate a certificate for student " + str(user.username) + " but with a grade of " + str(grade) + " and active status " + str(user.is_active)) log.warning("Asked to generate a certificate for student " + str(user.username) + " but with a grade of " + str(grade) + " and active status " + str(user.is_active))
...@@ -3,10 +3,11 @@ import uuid ...@@ -3,10 +3,11 @@ import uuid
from django.db import models from django.db import models
from django.contrib.auth.models import User from django.contrib.auth.models import User
class ServerCircuit(models.Model): class ServerCircuit(models.Model):
# Later, add owner, who can edit, part of what app, etc. # Later, add owner, who can edit, part of what app, etc.
name = models.CharField(max_length=32, unique=True, db_index=True) name = models.CharField(max_length=32, unique=True, db_index=True)
schematic = models.TextField(blank=True) schematic = models.TextField(blank=True)
def __unicode__(self): def __unicode__(self):
return self.name+":"+self.schematic[:8] return self.name + ":" + self.schematic[:8]
...@@ -11,6 +11,7 @@ from mitxmako.shortcuts import render_to_response, render_to_string ...@@ -11,6 +11,7 @@ from mitxmako.shortcuts import render_to_response, render_to_string
from models import ServerCircuit from models import ServerCircuit
def circuit_line(circuit): def circuit_line(circuit):
''' Returns string for an appropriate input element for a circuit. ''' Returns string for an appropriate input element for a circuit.
TODO: Rename. ''' TODO: Rename. '''
...@@ -28,10 +29,11 @@ def circuit_line(circuit): ...@@ -28,10 +29,11 @@ def circuit_line(circuit):
circuit_line.set('width', '640') circuit_line.set('width', '640')
circuit_line.set('height', '480') circuit_line.set('height', '480')
circuit_line.set('name', 'schematic') circuit_line.set('name', 'schematic')
circuit_line.set('id', 'schematic_'+circuit) circuit_line.set('id', 'schematic_' + circuit)
circuit_line.set('value', schematic) # We do it this way for security -- guarantees users cannot put funny stuff in schematic. circuit_line.set('value', schematic) # We do it this way for security -- guarantees users cannot put funny stuff in schematic.
return xml.etree.ElementTree.tostring(circuit_line) return xml.etree.ElementTree.tostring(circuit_line)
def edit_circuit(request, circuit): def edit_circuit(request, circuit):
try: try:
sc = ServerCircuit.objects.get(name=circuit) sc = ServerCircuit.objects.get(name=circuit)
...@@ -40,11 +42,12 @@ def edit_circuit(request, circuit): ...@@ -40,11 +42,12 @@ def edit_circuit(request, circuit):
if not circuit.isalnum(): if not circuit.isalnum():
raise Http404() raise Http404()
response = render_to_response('edit_circuit.html', {'name':circuit, response = render_to_response('edit_circuit.html', {'name': circuit,
'circuit_line':circuit_line(circuit)}) 'circuit_line': circuit_line(circuit)})
response['Cache-Control'] = 'no-cache' response['Cache-Control'] = 'no-cache'
return response return response
def save_circuit(request, circuit): def save_circuit(request, circuit):
if not circuit.isalnum(): if not circuit.isalnum():
raise Http404() raise Http404()
...@@ -63,4 +66,3 @@ def save_circuit(request, circuit): ...@@ -63,4 +66,3 @@ def save_circuit(request, circuit):
response = HttpResponse(json_str, mimetype='application/json') response = HttpResponse(json_str, mimetype='application/json')
response['Cache-Control'] = 'no-cache' response['Cache-Control'] = 'no-cache'
return response return response
...@@ -67,18 +67,16 @@ class Settings(object): ...@@ -67,18 +67,16 @@ class Settings(object):
# Load the global settings as a dictionary # Load the global settings as a dictionary
global_settings = json.loads(global_settings_json) global_settings = json.loads(global_settings_json)
# Load the course settings as a dictionary # Load the course settings as a dictionary
course_settings = {} course_settings = {}
try: try:
# TODO: this doesn't work with multicourse # TODO: this doesn't work with multicourse
with open( settings.DATA_DIR + "/course_settings.json") as course_settings_file: with open(settings.DATA_DIR + "/course_settings.json") as course_settings_file:
course_settings_string = course_settings_file.read() course_settings_string = course_settings_file.read()
course_settings = json.loads(course_settings_string) course_settings = json.loads(course_settings_string)
except IOError: except IOError:
log.warning("Unable to load course settings file from " + str(settings.DATA_DIR) + "/course_settings.json") log.warning("Unable to load course settings file from " + str(settings.DATA_DIR) + "/course_settings.json")
# Override any global settings with the course settings # Override any global settings with the course settings
global_settings.update(course_settings) global_settings.update(course_settings)
......
...@@ -12,6 +12,7 @@ from xmodule.modulestore.exceptions import ItemNotFoundError ...@@ -12,6 +12,7 @@ from xmodule.modulestore.exceptions import ItemNotFoundError
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
def check_course(course_id, course_must_be_open=True, course_required=True): def check_course(course_id, course_must_be_open=True, course_required=True):
""" """
Given a course_id, this returns the course object. By default, Given a course_id, this returns the course object. By default,
...@@ -45,9 +46,11 @@ def check_course(course_id, course_must_be_open=True, course_required=True): ...@@ -45,9 +46,11 @@ def check_course(course_id, course_must_be_open=True, course_required=True):
def course_static_url(course): def course_static_url(course):
return settings.STATIC_URL + "/" + course.metadata['data_dir'] + "/" return settings.STATIC_URL + "/" + course.metadata['data_dir'] + "/"
def course_image_url(course): def course_image_url(course):
return course_static_url(course) + "images/course_image.jpg" return course_static_url(course) + "images/course_image.jpg"
def get_course_about_section(course, section_key): def get_course_about_section(course, section_key):
""" """
This returns the snippet of html to be rendered on the course about page, given the key for the section. This returns the snippet of html to be rendered on the course about page, given the key for the section.
...@@ -78,7 +81,7 @@ def get_course_about_section(course, section_key): ...@@ -78,7 +81,7 @@ def get_course_about_section(course, section_key):
'effort', 'end_date', 'prerequisites']: 'effort', 'end_date', 'prerequisites']:
try: try:
with course.system.resources_fs.open(path("about") / section_key + ".html") as htmlFile: with course.system.resources_fs.open(path("about") / section_key + ".html") as htmlFile:
return htmlFile.read().decode('utf-8').format(COURSE_STATIC_URL = course_static_url(course) ) return htmlFile.read().decode('utf-8').format(COURSE_STATIC_URL=course_static_url(course))
except ResourceNotFoundError: except ResourceNotFoundError:
log.warning("Missing about section {key} in course {url}".format(key=section_key, url=course.location.url())) log.warning("Missing about section {key} in course {url}".format(key=section_key, url=course.location.url()))
return None return None
...@@ -91,6 +94,7 @@ def get_course_about_section(course, section_key): ...@@ -91,6 +94,7 @@ def get_course_about_section(course, section_key):
raise KeyError("Invalid about key " + str(section_key)) raise KeyError("Invalid about key " + str(section_key))
def get_course_info_section(course, section_key): def get_course_info_section(course, section_key):
""" """
This returns the snippet of html to be rendered on the course info page, given the key for the section. This returns the snippet of html to be rendered on the course info page, given the key for the section.
......
...@@ -4,6 +4,7 @@ from south.db import db ...@@ -4,6 +4,7 @@ from south.db import db
from south.v2 import SchemaMigration from south.v2 import SchemaMigration
from django.db import models from django.db import models
class Migration(SchemaMigration): class Migration(SchemaMigration):
def forwards(self, orm): def forwards(self, orm):
...@@ -24,7 +25,6 @@ class Migration(SchemaMigration): ...@@ -24,7 +25,6 @@ class Migration(SchemaMigration):
# Adding unique constraint on 'StudentModule', fields ['student', 'module_id', 'module_type'] # Adding unique constraint on 'StudentModule', fields ['student', 'module_id', 'module_type']
db.create_unique('courseware_studentmodule', ['student_id', 'module_id', 'module_type']) db.create_unique('courseware_studentmodule', ['student_id', 'module_id', 'module_type'])
def backwards(self, orm): def backwards(self, orm):
# Removing unique constraint on 'StudentModule', fields ['student', 'module_id', 'module_type'] # Removing unique constraint on 'StudentModule', fields ['student', 'module_id', 'module_type']
...@@ -33,7 +33,6 @@ class Migration(SchemaMigration): ...@@ -33,7 +33,6 @@ class Migration(SchemaMigration):
# Deleting model 'StudentModule' # Deleting model 'StudentModule'
db.delete_table('courseware_studentmodule') db.delete_table('courseware_studentmodule')
models = { models = {
'auth.group': { 'auth.group': {
'Meta': {'object_name': 'Group'}, 'Meta': {'object_name': 'Group'},
......
...@@ -4,6 +4,7 @@ from south.db import db ...@@ -4,6 +4,7 @@ from south.db import db
from south.v2 import SchemaMigration from south.v2 import SchemaMigration
from django.db import models from django.db import models
class Migration(SchemaMigration): class Migration(SchemaMigration):
def forwards(self, orm): def forwards(self, orm):
...@@ -23,7 +24,6 @@ class Migration(SchemaMigration): ...@@ -23,7 +24,6 @@ class Migration(SchemaMigration):
# Adding index on 'StudentModule', fields ['module_id'] # Adding index on 'StudentModule', fields ['module_id']
db.create_index('courseware_studentmodule', ['module_id']) db.create_index('courseware_studentmodule', ['module_id'])
def backwards(self, orm): def backwards(self, orm):
# Removing index on 'StudentModule', fields ['module_id'] # Removing index on 'StudentModule', fields ['module_id']
...@@ -41,7 +41,6 @@ class Migration(SchemaMigration): ...@@ -41,7 +41,6 @@ class Migration(SchemaMigration):
# Removing index on 'StudentModule', fields ['created'] # Removing index on 'StudentModule', fields ['created']
db.delete_index('courseware_studentmodule', ['created']) db.delete_index('courseware_studentmodule', ['created'])
models = { models = {
'auth.group': { 'auth.group': {
'Meta': {'object_name': 'Group'}, 'Meta': {'object_name': 'Group'},
......
...@@ -4,6 +4,7 @@ from south.db import db ...@@ -4,6 +4,7 @@ from south.db import db
from south.v2 import SchemaMigration from south.v2 import SchemaMigration
from django.db import models from django.db import models
class Migration(SchemaMigration): class Migration(SchemaMigration):
def forwards(self, orm): def forwards(self, orm):
...@@ -20,7 +21,6 @@ class Migration(SchemaMigration): ...@@ -20,7 +21,6 @@ class Migration(SchemaMigration):
# Adding unique constraint on 'StudentModule', fields ['module_id', 'student'] # Adding unique constraint on 'StudentModule', fields ['module_id', 'student']
db.create_unique('courseware_studentmodule', ['module_id', 'student_id']) db.create_unique('courseware_studentmodule', ['module_id', 'student_id'])
def backwards(self, orm): def backwards(self, orm):
# Removing unique constraint on 'StudentModule', fields ['module_id', 'student'] # Removing unique constraint on 'StudentModule', fields ['module_id', 'student']
...@@ -35,7 +35,6 @@ class Migration(SchemaMigration): ...@@ -35,7 +35,6 @@ class Migration(SchemaMigration):
# Adding unique constraint on 'StudentModule', fields ['module_id', 'module_type', 'student'] # Adding unique constraint on 'StudentModule', fields ['module_id', 'module_type', 'student']
db.create_unique('courseware_studentmodule', ['module_id', 'module_type', 'student_id']) db.create_unique('courseware_studentmodule', ['module_id', 'module_type', 'student_id'])
models = { models = {
'auth.group': { 'auth.group': {
'Meta': {'object_name': 'Group'}, 'Meta': {'object_name': 'Group'},
......
...@@ -63,7 +63,6 @@ class StudentModule(models.Model): ...@@ -63,7 +63,6 @@ class StudentModule(models.Model):
# TODO (cpennington): Remove these once the LMS switches to using XModuleDescriptors # TODO (cpennington): Remove these once the LMS switches to using XModuleDescriptors
class StudentModuleCache(object): class StudentModuleCache(object):
""" """
A cache of StudentModules for a specific student A cache of StudentModules for a specific student
...@@ -84,7 +83,7 @@ class StudentModuleCache(object): ...@@ -84,7 +83,7 @@ class StudentModuleCache(object):
# that can be put into a single query # that can be put into a single query
self.cache = [] self.cache = []
chunk_size = 500 chunk_size = 500
for id_chunk in [module_ids[i:i+chunk_size] for i in xrange(0, len(module_ids), chunk_size)]: for id_chunk in [module_ids[i:i + chunk_size] for i in xrange(0, len(module_ids), chunk_size)]:
self.cache.extend(StudentModule.objects.filter( self.cache.extend(StudentModule.objects.filter(
student=user, student=user,
module_state_key__in=id_chunk) module_state_key__in=id_chunk)
......
...@@ -308,9 +308,9 @@ def add_histogram(module): ...@@ -308,9 +308,9 @@ def add_histogram(module):
coursename = multicourse_settings.get_coursename_from_request(request) coursename = multicourse_settings.get_coursename_from_request(request)
github_url = multicourse_settings.get_course_github_url(coursename) github_url = multicourse_settings.get_course_github_url(coursename)
fn = module_xml.get('filename') fn = module_xml.get('filename')
if module_xml.tag=='problem': fn = 'problems/' + fn # grrr if module_xml.tag == 'problem': fn = 'problems/' + fn # grrr
edit_link = (github_url + '/tree/master/' + fn) if github_url is not None else None edit_link = (github_url + '/tree/master/' + fn) if github_url is not None else None
if module_xml.tag=='problem': edit_link += '.xml' # grrr if module_xml.tag == 'problem': edit_link += '.xml' # grrr
else: else:
edit_link = False edit_link = False
...@@ -328,6 +328,7 @@ def add_histogram(module): ...@@ -328,6 +328,7 @@ def add_histogram(module):
module.get_html = get_html module.get_html = get_html
return module return module
# TODO: TEMPORARY BYPASS OF AUTH! # TODO: TEMPORARY BYPASS OF AUTH!
@csrf_exempt @csrf_exempt
def xqueue_callback(request, userid, id, dispatch): def xqueue_callback(request, userid, id, dispatch):
...@@ -374,6 +375,7 @@ def xqueue_callback(request, userid, id, dispatch): ...@@ -374,6 +375,7 @@ def xqueue_callback(request, userid, id, dispatch):
return HttpResponse("") return HttpResponse("")
def modx_dispatch(request, dispatch=None, id=None): def modx_dispatch(request, dispatch=None, id=None):
''' Generic view for extensions. This is where AJAX calls go. ''' Generic view for extensions. This is where AJAX calls go.
......
class completion(object): class completion(object):
def __init__(self, **d): def __init__(self, **d):
self.dict = dict({'duration_total':0, self.dict = dict({'duration_total': 0,
'duration_watched':0, 'duration_watched': 0,
'done':True, 'done': True,
'questions_correct':0, 'questions_correct': 0,
'questions_incorrect':0, 'questions_incorrect': 0,
'questions_total':0}) 'questions_total': 0})
if d: if d:
self.dict.update(d) self.dict.update(d)
...@@ -23,7 +23,7 @@ class completion(object): ...@@ -23,7 +23,7 @@ class completion(object):
'questions_correct', 'questions_correct',
'questions_incorrect', 'questions_incorrect',
'questions_total']: 'questions_total']:
result[item] = result[item]+other.dict[item] result[item] = result[item] + other.dict[item]
return completion(**result) return completion(**result)
def __contains__(self, key): def __contains__(self, key):
...@@ -33,6 +33,6 @@ class completion(object): ...@@ -33,6 +33,6 @@ class completion(object):
return repr(self.dict) return repr(self.dict)
if __name__ == '__main__': if __name__ == '__main__':
dict1=completion(duration_total=5) dict1 = completion(duration_total=5)
dict2=completion(duration_total=7) dict2 = completion(duration_total=7)
print dict1+dict2 print dict1 + dict2
...@@ -31,6 +31,7 @@ log = logging.getLogger("mitx.courseware") ...@@ -31,6 +31,7 @@ log = logging.getLogger("mitx.courseware")
template_imports = {'urllib': urllib} template_imports = {'urllib': urllib}
def user_groups(user): def user_groups(user):
if not user.is_authenticated(): if not user.is_authenticated():
return [] return []
...@@ -62,7 +63,8 @@ def courses(request): ...@@ -62,7 +63,8 @@ def courses(request):
for course in courses: for course in courses:
universities[course.org].append(course) universities[course.org].append(course)
return render_to_response("courses.html", { 'universities': universities }) return render_to_response("courses.html", {'universities': universities})
@cache_control(no_cache=True, no_store=True, must_revalidate=True) @cache_control(no_cache=True, no_store=True, must_revalidate=True)
def gradebook(request, course_id): def gradebook(request, course_id):
...@@ -70,7 +72,6 @@ def gradebook(request, course_id): ...@@ -70,7 +72,6 @@ def gradebook(request, course_id):
raise Http404 raise Http404
course = check_course(course_id) course = check_course(course_id)
student_objects = User.objects.all()[:100] student_objects = User.objects.all()[:100]
student_info = [] student_info = []
...@@ -258,12 +259,13 @@ def course_info(request, course_id): ...@@ -258,12 +259,13 @@ def course_info(request, course_id):
return render_to_response('info.html', {'course': course}) return render_to_response('info.html', {'course': course})
@ensure_csrf_cookie @ensure_csrf_cookie
@cache_if_anonymous @cache_if_anonymous
def course_about(request, course_id): def course_about(request, course_id):
def registered_for_course(course, user): def registered_for_course(course, user):
if user.is_authenticated(): if user.is_authenticated():
return CourseEnrollment.objects.filter(user = user, course_id=course.id).exists() return CourseEnrollment.objects.filter(user=user, course_id=course.id).exists()
else: else:
return False return False
course = check_course(course_id, course_must_be_open=False) course = check_course(course_id, course_must_be_open=False)
...@@ -271,7 +273,6 @@ def course_about(request, course_id): ...@@ -271,7 +273,6 @@ def course_about(request, course_id):
return render_to_response('portal/course_about.html', {'course': course, 'registered': registered}) return render_to_response('portal/course_about.html', {'course': course, 'registered': registered})
@ensure_csrf_cookie @ensure_csrf_cookie
@cache_if_anonymous @cache_if_anonymous
def university_profile(request, org_id): def university_profile(request, org_id):
...@@ -281,7 +282,7 @@ def university_profile(request, org_id): ...@@ -281,7 +282,7 @@ def university_profile(request, org_id):
raise Http404("University Profile not found for {0}".format(org_id)) raise Http404("University Profile not found for {0}".format(org_id))
# Only grab courses for this org... # Only grab courses for this org...
courses=[c for c in all_courses if c.org == org_id] courses = [c for c in all_courses if c.org == org_id]
context = dict(courses=courses, org_id=org_id) context = dict(courses=courses, org_id=org_id)
template_file = "university_profile/{0}.html".format(org_id).lower() template_file = "university_profile/{0}.html".format(org_id).lower()
......
...@@ -2,6 +2,7 @@ import json ...@@ -2,6 +2,7 @@ import json
from datetime import datetime from datetime import datetime
from django.http import HttpResponse from django.http import HttpResponse
def heartbeat(request): def heartbeat(request):
""" """
Simple view that a loadbalancer can check to verify that the app is up Simple view that a loadbalancer can check to verify that the app is up
......
...@@ -25,10 +25,10 @@ from django.conf import settings ...@@ -25,10 +25,10 @@ from django.conf import settings
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# load course settings # load course settings
if hasattr(settings,'COURSE_SETTINGS'): # in the future, this could be replaced by reading an XML file if hasattr(settings, 'COURSE_SETTINGS'): # in the future, this could be replaced by reading an XML file
COURSE_SETTINGS = settings.COURSE_SETTINGS COURSE_SETTINGS = settings.COURSE_SETTINGS
elif hasattr(settings,'COURSE_NAME'): # backward compatibility elif hasattr(settings, 'COURSE_NAME'): # backward compatibility
COURSE_SETTINGS = {settings.COURSE_NAME: {'number': settings.COURSE_NUMBER, COURSE_SETTINGS = {settings.COURSE_NAME: {'number': settings.COURSE_NUMBER,
'title': settings.COURSE_TITLE, 'title': settings.COURSE_TITLE,
'location': settings.COURSE_LOCATION, 'location': settings.COURSE_LOCATION,
...@@ -44,6 +44,7 @@ else: # default to 6.002_Spring_2012 ...@@ -44,6 +44,7 @@ else: # default to 6.002_Spring_2012
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# wrapper functions around course settings # wrapper functions around course settings
def get_coursename_from_request(request): def get_coursename_from_request(request):
if 'coursename' in request.session: if 'coursename' in request.session:
coursename = request.session['coursename'] coursename = request.session['coursename']
...@@ -51,6 +52,7 @@ def get_coursename_from_request(request): ...@@ -51,6 +52,7 @@ def get_coursename_from_request(request):
else: coursename = None else: coursename = None
return coursename return coursename
def get_course_settings(coursename): def get_course_settings(coursename):
if not coursename: if not coursename:
if hasattr(settings, 'COURSE_DEFAULT'): if hasattr(settings, 'COURSE_DEFAULT'):
...@@ -94,14 +96,18 @@ def get_course_title(coursename): ...@@ -94,14 +96,18 @@ def get_course_title(coursename):
def get_course_number(coursename): def get_course_number(coursename):
return get_course_property(coursename, 'number') return get_course_property(coursename, 'number')
def get_course_github_url(coursename): def get_course_github_url(coursename):
return get_course_property(coursename,'github_url') return get_course_property(coursename, 'github_url')
def get_course_default_chapter(coursename): def get_course_default_chapter(coursename):
return get_course_property(coursename,'default_chapter') return get_course_property(coursename, 'default_chapter')
def get_course_default_section(coursename): def get_course_default_section(coursename):
return get_course_property(coursename,'default_section') return get_course_property(coursename, 'default_section')
def get_course_location(coursename): def get_course_location(coursename):
return get_course_property(coursename, 'location') return get_course_property(coursename, 'location')
...@@ -3,12 +3,12 @@ from mitxmako.shortcuts import render_to_response ...@@ -3,12 +3,12 @@ from mitxmako.shortcuts import render_to_response
from multicourse import multicourse_settings from multicourse import multicourse_settings
def mitxhome(request): def mitxhome(request):
''' Home page (link from main header). List of courses. ''' ''' Home page (link from main header). List of courses. '''
if settings.DEBUG: if settings.DEBUG:
print "[djangoapps.multicourse.mitxhome] MITX_ROOT_URL = " + settings.MITX_ROOT_URL print "[djangoapps.multicourse.mitxhome] MITX_ROOT_URL = " + settings.MITX_ROOT_URL
if settings.ENABLE_MULTICOURSE: if settings.ENABLE_MULTICOURSE:
context = {'courseinfo' : multicourse_settings.COURSE_SETTINGS} context = {'courseinfo': multicourse_settings.COURSE_SETTINGS}
return render_to_response("mitxhome.html", context) return render_to_response("mitxhome.html", context)
return info(request) return info(request)
...@@ -6,17 +6,21 @@ from django.utils.translation import ugettext as _ ...@@ -6,17 +6,21 @@ from django.utils.translation import ugettext as _
from models import Article, Revision, Permission, ArticleAttachment from models import Article, Revision, Permission, ArticleAttachment
class RevisionInline(admin.TabularInline): class RevisionInline(admin.TabularInline):
model = Revision model = Revision
extra = 1 extra = 1
class RevisionAdmin(admin.ModelAdmin): class RevisionAdmin(admin.ModelAdmin):
list_display = ('article', '__unicode__', 'revision_date', 'revision_user', 'revision_text') list_display = ('article', '__unicode__', 'revision_date', 'revision_user', 'revision_text')
search_fields = ('article', 'counter') search_fields = ('article', 'counter')
class AttachmentAdmin(admin.ModelAdmin): class AttachmentAdmin(admin.ModelAdmin):
list_display = ('article', '__unicode__', 'uploaded_on', 'uploaded_by') list_display = ('article', '__unicode__', 'uploaded_on', 'uploaded_by')
class ArticleAdminForm(forms.ModelForm): class ArticleAdminForm(forms.ModelForm):
def clean(self): def clean(self):
cleaned_data = self.cleaned_data cleaned_data = self.cleaned_data
...@@ -30,16 +34,19 @@ class ArticleAdminForm(forms.ModelForm): ...@@ -30,16 +34,19 @@ class ArticleAdminForm(forms.ModelForm):
raise forms.ValidationError(_('Article slug and parent must be ' raise forms.ValidationError(_('Article slug and parent must be '
'unique together.')) 'unique together.'))
return cleaned_data return cleaned_data
class Meta: class Meta:
model = Article model = Article
class ArticleAdmin(admin.ModelAdmin): class ArticleAdmin(admin.ModelAdmin):
list_display = ('created_by', 'slug', 'modified_on', 'namespace') list_display = ('created_by', 'slug', 'modified_on', 'namespace')
search_fields = ('slug',) search_fields = ('slug',)
prepopulated_fields = {'slug': ('title',) } prepopulated_fields = {'slug': ('title',)}
inlines = [RevisionInline] inlines = [RevisionInline]
form = ArticleAdminForm form = ArticleAdminForm
save_on_top = True save_on_top = True
def formfield_for_foreignkey(self, db_field, request, **kwargs): def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name == 'current_revision': if db_field.name == 'current_revision':
# Try to determine the id of the article being edited # Try to determine the id of the article being edited
...@@ -53,6 +60,7 @@ class ArticleAdmin(admin.ModelAdmin): ...@@ -53,6 +60,7 @@ class ArticleAdmin(admin.ModelAdmin):
return db_field.formfield(**kwargs) return db_field.formfield(**kwargs)
return super(ArticleAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs) return super(ArticleAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
class PermissionAdmin(admin.ModelAdmin): class PermissionAdmin(admin.ModelAdmin):
search_fields = ('article', 'counter') search_fields = ('article', 'counter')
......
...@@ -26,12 +26,12 @@ try: ...@@ -26,12 +26,12 @@ try:
except: except:
from markdown import etree from markdown import etree
class CircuitExtension(markdown.Extension): class CircuitExtension(markdown.Extension):
def __init__(self, configs): def __init__(self, configs):
for key, value in configs : for key, value in configs:
self.setConfig(key, value) self.setConfig(key, value)
def extendMarkdown(self, md, md_globals): def extendMarkdown(self, md, md_globals):
## Because Markdown treats contigous lines as one block of text, it is hard to match ## Because Markdown treats contigous lines as one block of text, it is hard to match
## a regex that must occupy the whole line (like the circuit regex). This is why we have ## a regex that must occupy the whole line (like the circuit regex). This is why we have
...@@ -52,11 +52,11 @@ class CircuitPreprocessor(markdown.preprocessors.Preprocessor): ...@@ -52,11 +52,11 @@ class CircuitPreprocessor(markdown.preprocessors.Preprocessor):
def convertLine(line): def convertLine(line):
m = self.preRegex.match(line) m = self.preRegex.match(line)
if m: if m:
return 'processed-schematic:{0}processed-schematic-end'.format( m.group('data') ) return 'processed-schematic:{0}processed-schematic-end'.format(m.group('data'))
else: else:
return line return line
return [ convertLine(line) for line in lines ] return [convertLine(line) for line in lines]
class CircuitLink(markdown.inlinepatterns.Pattern): class CircuitLink(markdown.inlinepatterns.Pattern):
...@@ -68,5 +68,5 @@ class CircuitLink(markdown.inlinepatterns.Pattern): ...@@ -68,5 +68,5 @@ class CircuitLink(markdown.inlinepatterns.Pattern):
def makeExtension(configs=None): def makeExtension(configs=None):
to_return = CircuitExtension(configs=configs) to_return = CircuitExtension(configs=configs)
print "circuit returning " , to_return print "circuit returning ", to_return
return to_return return to_return
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