Skip to content
Snippets Groups Projects
Commit cc1de22e authored by Piotr Mitros's avatar Piotr Mitros
Browse files

Initial commit

parents
No related branches found
No related tags found
No related merge requests found
Showing
with 469 additions and 0 deletions
syntax: glob
*.pyc
*~
database.sqlite
from django.db import models
from django.contrib.auth.models import User
import uuid
class UserProfile(models.Model):
user = models.ForeignKey(User, unique=True, db_index=True)
name = models.TextField(blank=True)
language = models.TextField(blank=True)
location = models.TextField(blank=True)
meta = models.TextField(blank=True) # JSON dictionary for future expansion
class Registration(models.Model):
''' Allows us to wait for e-mail before user is registered. A
registration profile is created when the user creates an
account, but that account is inactive. Once the user clicks
on the activation key, it becomes active. '''
user = models.ForeignKey(User, unique=True)
activation_key = models.CharField(('activation key'), max_length=32, unique=True, db_index=True)
def register(self, user):
# MINOR TODO: Switch to crypto-secure key
self.activation_key=uuid.uuid4().hex
self.user=user
self.save()
def activate(self):
self.user.is_active = True
self.user.save()
self.delete()
"""
This file demonstrates writing tests using the unittest module. These will pass
when you run "manage.py test".
Replace this with more appropriate tests for your application.
"""
from django.test import TestCase
class SimpleTest(TestCase):
def test_basic_addition(self):
"""
Tests that 1 + 1 always equals 2.
"""
self.assertEqual(1 + 1, 2)
from djangomako.shortcuts import render_to_response, render_to_string
from django.contrib.auth.models import User
from django.shortcuts import redirect
from django.contrib.auth import logout, authenticate, login
from django.contrib.auth.models import User
from django.http import HttpResponse
import json
from models import Registration, UserProfile
from django.conf import settings
from django.core.context_processors import csrf
from django.core.validators import validate_email, validate_slug
def csrf_token(context):
csrf_token = context.get('csrf_token', '')
if csrf_token == 'NOTPROVIDED':
return ''
return u'<div style="display:none"><input type="hidden" name="csrfmiddlewaretoken" value="%s" /></div>' % (csrf_token)
def index(request):
if request.user.is_authenticated():
return redirect('/courseware')
else:
return render_to_response('index.html', {'error':'', 'csrf':csrf(request)['csrf_token']}) # Clean up how error is done.
def login_user(request, error=""):
if 'email' not in request.GET or 'password' not in request.GET:
return render_to_response('login.html', {'error':error.replace('+',' ')})
email = request.GET['email']
password = request.GET['password']
try:
user=User.objects.get(email=email)
except User.DoesNotExist:
return HttpResponse(json.dumps({'success':False, 'error': 'Invalid login'})) # TODO: User error message
username=user.username
user=authenticate(username=username, password=password)
if user is None:
return HttpResponse(json.dumps({'success':False, 'error': 'Invalid login'}))
if user is not None and user.is_active:
login(request, user)
return HttpResponse(json.dumps({'success':True}))
return HttpResponse(json.dumps({'success':False, 'error': 'Account not active. Check your e-mail.'}))
def logout_user(request):
logout(request)
return redirect('/')
def create_account(request):
js={'success':False}
# Confirm we have a properly formed request
for a in ['username', 'email', 'password', 'location', 'language', 'name']:
if a not in request.GET:
js['value']="Error (401 {field}). E-mail us.".format(field=a)
return HttpResponse(json.dumps(js))
if request.GET['honor_code']!=u'true':
js['value']="To enroll, you must follow the honor code.".format(field=a)
return HttpResponse(json.dumps(js))
if request.GET['terms_of_service']!=u'true':
js['value']="You must accept the terms of service.".format(field=a)
return HttpResponse(json.dumps(js))
# Confirm appropriate fields are there.
# TODO: Check e-mail format is correct.
# TODO: Confirm e-mail is not from a generic domain (mailinator, etc.)? Not sure if
# this is a good idea
# TODO: Check password is sane
for a in ['username', 'email', 'password', 'terms_of_service', 'honor_code']:
if len(request.GET[a])<2:
js['value']="{field} is required.".format(field=a)
return HttpResponse(json.dumps(js))
try:
validate_email(request.GET['email'])
except:
js['value']="Valid e-mail is required.".format(field=a)
return HttpResponse(json.dumps(js))
try:
validate_slug(request.GET['username'])
except:
js['value']="Username should only consist of A-Z and 0-9.".format(field=a)
return HttpResponse(json.dumps(js))
# Confirm username and e-mail are unique. TODO: This should be in a transaction
if len(User.objects.filter(username=request.GET['username']))>0:
js['value']="An account with this username already exists."
return HttpResponse(json.dumps(js))
if len(User.objects.filter(email=request.GET['email']))>0:
js['value']="An account with this e-mail already exists."
return HttpResponse(json.dumps(js))
u=User(username=request.GET['username'],
email=request.GET['email'],
is_active=False)
u.set_password(request.GET['password'])
r=Registration()
# TODO: Rearrange so that if part of the process fails, the whole process fails.
# Right now, we can have e.g. no registration e-mail sent out and a zombie account
u.save()
r.register(u)
up=UserProfile(user=u)
up.name=request.GET['name']
up.language=request.GET['language']
up.location=request.GET['location']
up.save()
d={'name':request.GET['name'],
'key':r.activation_key,
'site':settings.SITE_NAME}
subject = render_to_string('activation_email_subject.txt',d)
# Email subject *must not* contain newlines
subject = ''.join(subject.splitlines())
message = render_to_string('activation_email.txt',d)
try:
res=u.email_user(subject, message, settings.DEFAULT_FROM_EMAIL)
except:
js['value']=str(sys.exc_info())
return HttpResponse(json.dumps(js))
js={'success':True,
'value':render_to_string('registration/reg_complete.html', {'email':request.GET['email']})}
return HttpResponse(json.dumps(js), mimetype="application/json")
def activate_account(request, key):
r=Registration.objects.filter(activation_key=key)
if len(r)==1:
r[0].activate()
return render_to_response("activation_complete.html",{})
if len(r)==0:
return render_to_response("activation_invalid.html",{})
return HttpResponse("Unknown error. Please e-mail us to let us know how it happened.")
# For calculator:
# http://pyparsing.wikispaces.com/file/view/fourFn.py
import random, numpy, math, scipy, sys, StringIO, os, struct, json
from x_module import XModule
from xml.dom.minidom import parse, parseString
class LoncapaProblem(XModule):
def get_state(self):
def get_score(self):
def max_score(self):
def get_html(self):
def handle_ajax(self, json):
def __init__(self, filename, id=None, state=None):
if __name__=='__main__':
p=LoncapaProblem('<problem name="Problem 1: Resistive Divider" filename="resistor" />')
print p.getHtml()
print p.getContext()
print p.getSeed()
# Chef and puppot
# For calculator:
# http://pyparsing.wikispaces.com/file/view/fourFn.py
import random, numpy, math, scipy, sys, StringIO, os, struct, json
from xml.dom.minidom import parse, parseString
class LoncapaProblem():
def get_state(self):
''' Stored per-user session data neeeded to:
1) Recreate the problem
2) Populate any student answers. '''
return json.dumps({'seed':self.seed,
'answers':self.answers,
'correct_map':self.correct_map})
def get_score(self):
correct=0
for key in self.correct_map:
if self.correct_map[key] == u'correct':
correct += 1
if len(self.answers)==0:
return {'score':0,
'total':len(self.questions)}
else:
return {'score':correct,
'total':len(self.questions)}
def max_score(self):
pass
def get_html(self):
''' Return the HTML of the question '''
return self.text
def handle_ajax(self, json):
pass
def __init__(self, filename, id=None, state=None):
''' Create a new problem of the type defined in filename.
By default, this will generate a random problem. Passing
seed will provide the random seed. Alternatively, passing
context will bypass all script execution, and use the
given execution context. '''
if state!=None:
state=json.loads(state)
else:
state={}
self.gid=id
if 'seed' in state:
self.seed=state['seed']
else:
# TODO: Check performance of urandom -- depending on
# implementation, it may slow down to the point of causing
# performance issues if we deplete the kernel entropy
# pool.
self.seed=struct.unpack('i', os.urandom(4))[0]
if 'answers' in state:
self.answers=state['answers']
if 'correct_map' in state:
self.correct_map=state['correct_map']
random.seed(self.seed)
dom=parse(filename).childNodes[0]
g={'random':random,'numpy':numpy,'math':math,'scipy':scipy}
buf=StringIO.StringIO()
ot=False ## Are we in an outtext context?
for e in dom.childNodes:
if e.localName=='script' and context==None:
exec e.childNodes[0].data in g,self.context
if e.localName=='endouttext':
ot=False
if ot:
e.writexml(buf)
if e.localName=='startouttext':
ot=True
if e.localName in self.handlers:
problem=self.handlers[e.localName](self,e)
buf.write(problem)
self.text=buf.getvalue()
self.text=self.contextualize_text(self.text)
text=""
context={} # Execution context from loncapa/python
questions={} # Detailed info about questions in problem instance. TODO: Should be by id and not lid.
answers={} # Student answers
correct_map={}
seed=None
gid="" # ID of the problem
lid=-1 # ID of the field within the problem
def get_context(self):
''' Return the execution context '''
return self.context
def get_seed(self):
''' Return the random seed used to generate the problem '''
return self.seed
def get_correct_map(self):
return self.correct_map
def set_answers(self, answers):
self.answers=answers
def grade_answers(self, answers):
''' Takes a map of IDs to answers. Return which ones are correct '''
self.answers=answers
correct_map={}
for key in self.questions:
id=self.questions[key]['id']
if id not in answers:
correct_map[id]='incorrect' # Should always be there
else:
correct_map[id]=self.grade_nr(self.questions[key],
self.answers[id])
self.correct_map=correct_map
return correct_map
## Internal methods
def number(self,text):
''' Convert a number to a float, understanding suffixes '''
try:
text.strip()
suffixes={'%':0.01,'k':1e3,'M':1e6,'G':1e9,'T':1e12,'P':1e15,
'E':1e18,'Z':1e21,'Y':1e24,'c':1e-2,'m':1e-3,'u':1e-6,
'n':1e-9,'p':1e-12,'f':1e-15,'a':1e-18,'z':1e-21,'y':1e-24}
if text[-1] in suffixes:
return float(text[:-1])*suffixes[text[-1]]
else:
return float(text)
except:
return 0 # TODO: Better error handling?
def grade_nr(self, question, answer):
error = abs(self.number(answer) - question['answer'])
allowed_error = abs(question['answer']*question['tolerance'])
if error <= allowed_error:
return 'correct'
else:
return 'incorrect'
def handle_nr(self, element):
answer=element.getAttribute('answer')
for e in element.childNodes:
if e.nodeType==1 and e.getAttribute('type')=="tolerance":
tolerance=e.getAttribute('default')
self.lid+=1
id=str(self.gid)+'_'+str(self.lid)
problem={"answer":self.number(self.contextualize_text(answer)),
"type":"numericalresponse",
"tolerance":self.number(self.contextualize_text(tolerance)),
"id":id,
"lid":self.lid,
}
self.questions[self.lid]=problem
if id in self.answers:
value=self.answers[id]
else:
value=""
icon='bullet'
if id in self.correct_map and self.correct_map[id]=='correct':
icon='check'
if id in self.correct_map and self.correct_map[id]=='incorrect':
icon='close'
html='<input type="text" name="input_{id}" id="input_{id}" value="{value}"><span class="ui-icon ui-icon-{icon}" style="display:inline-block;" id="status_{id}"></span> '.format(id=id,value=value,icon=icon)
return html
graders={'numericalresponse':grade_nr}
handlers={'numericalresponse':handle_nr}
def contextualize_text(self, text):
''' Takes a string with variables. E.g. $a+$b.
Does a substitution of those variables from the context '''
for key in sorted(self.context, lambda x,y:cmp(len(y),len(x))):
text=text.replace('$'+key, str(self.context[key]))
return text
if __name__=='__main__':
p=LoncapaProblem('resistor.xml', seed=-1601461296)
print p.getHtml()
print p.getContext()
print p.getSeed()
from django.db import models
from django.contrib.auth.models import User
# class Organization(models.Model):
# # Tree structure implemented such that child node has left ID
# # greater than all parents, and right ID less than all parents
# left_tree_id = models.IntegerField(unique=True, db_index=True)
# right_tree_id = models.IntegerField(unique=True, db_index=True)
# # This is a duplicate, but we keep this to enforce unique name
# # constraint
# parent = models.ForeignKey('self', null=True, blank=True)
# name = models.CharField(max_length=200)
# ORG_TYPES= (('course','course'),
# ('chapter','chapter'),
# ('section','section'),)
# org_type = models.CharField(max_length=32, choices=ORG_TYPES)
# available = models.DateField(null=True, blank=True)
# due = models.DateField(null=True, blank=True)
# # JSON dictionary of metadata:
# # Time for a video, format of a section, etc.
# metadata = models.TextField(null=True, blank=True)
# class Modules(models.Model):
# MOD_TYPES = (('hw','homework'),
# ('vid','video_clip'),
# ('lay','layout'),
# (),)
# module_type = models.CharField(max_length=100)
# left_tree_id = models.IntegerField(unique=True, db_index=True)
# right_tree_id = models.IntegerField(unique=True, db_index=True)
# LAYOUT_TYPES = (('leaf','leaf'),
# ('tab','tab'),
# ('seq','sequential'),
# ('sim','simultaneous'),)
# layout_type = models.CharField(max_length=32, choices=LAYOUT_TYPES)
# data = models.TextField(null=True, blank=True)
#class HomeworkProblems(models.Model):
class StudentModule(models.Model):
# For a homework problem, contains a JSON
# object consisting of state
state = models.TextField(null=True, blank=True)
grade = models.FloatField(null=True, blank=True)
student = models.ForeignKey(User)
MODULE_TYPES = (('hw','homework'),)
module_type = models.CharField(max_length=32, choices=MODULE_TYPES, default='hw')
module_id = models.CharField(max_length=255) # Filename
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
class Meta:
unique_together = (('student', 'module_id'),)
courseware/static/Kirchhoff_files/07172609b59c136393705e4067de95d0.png

704 B

courseware/static/Kirchhoff_files/08d7bd7060be987d4da37b7fc263a740.png

630 B

courseware/static/Kirchhoff_files/17bbbd9b6e69b94dab881bacae540191.png

630 B

courseware/static/Kirchhoff_files/200px-Kirchhoff_voltage_law.svg.png

7.56 KiB

courseware/static/Kirchhoff_files/229253cd444bad52ccf237f182f18267.png

1007 B

courseware/static/Kirchhoff_files/912713fc906c190d03a73f02b2f738ab.png

646 B

courseware/static/Kirchhoff_files/98720898396d325be0abb463b68caf90.png

645 B

courseware/static/Kirchhoff_files/KCL.png

5.23 KiB

courseware/static/Kirchhoff_files/c449f0cd2e060f03076e28ae5f8f0a75.png

543 B

courseware/static/Kirchhoff_files/ec93733267512bc18567c04e5a728e24.png

228 B

0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment