import BaseHTTPServer
import pickle
import random
import math
import copy
import cgi
import time
import re
import threading
import urlparse
import sys
import decimal
HOST_NAME = 'localhost'
PORT_NUMBER = 4079
formula_templates = [
{"name":"--TEMPLATE--", "disabled":True, "generateInputs":"return {'x':int(math.floor(random.random()*1000)), 'y':int(math.floor(random.random()*1000))}", "formula":"z=x+y", "problem":"What is z?", "resultTemplate":"%z%", "checkResult":"return (int(z) == int(x)+int(y))"},
]
formulae = []
def nl2br(s):
return s.replace("\n", " ")
def tabifyCode(code):
return "".join(["\t"+line+"\n" for line in code.split("\n")])
def generateFormula(formula_template=None):
if formula_template == None:
formula_templates_inuse = [i for i,v in enumerate(formula_templates) if not v["disabled"]]
i = random.choice(formula_templates_inuse)
formula_template = formula_templates[i]
formula = {"template":copy.copy(formula_template), "solved":False}
exec("def generateInputs():\n"+tabifyCode(formula_template["generateInputs"]))
formula["inputs"] = generateInputs()
formulae.append(formula)
saveFormulae()
return len(formulae)-1
def _checkResult(code, args):
exec("def checkResult("+", ".join(args.keys())+"):\n"+tabifyCode(code))
return checkResult(**args)
def checkResult(id, formula, params):
values = {}
for k,v in params.iteritems():
if k == "template_id":
continue
k,i = k.rsplit("_", 1)
v = v[0]
if k in values:
if v != values[k]:
return False
else:
values[k] = v
#TODO: Check we have all the values we need and no more
formula["solved"] = _checkResult(formula["template"]["checkResult"], values)
if formula["solved"]:
def getValue(o):
return values[o.group(1)]
formula["solution"] = re.sub(r'%([a-zA-Z]+)%', getValue, formula["template"]["resultTemplate"])
saveFormulae()
return True
return False
def editTemplate(id, formula_template, params):
try:
name = params["name"][0]
disabled = params.has_key("disabled") and True or False
generateInputs = params["generateInputs"][0]
formula = params["formula"][0]
problem = params["problem"][0]
resultTemplate = params["resultTemplate"][0]
checkResult = params["checkResult"][0]
exec("def _generateInputs():\n"+tabifyCode(generateInputs))
exec("def _checkResult():\n"+tabifyCode(checkResult))
if params.has_key("test"):
formula_template = {}
formula_template["name"] = name
formula_template["disabled"] = bool(disabled)
formula_template["generateInputs"] = generateInputs
formula_template["formula"] = formula
formula_template["problem"] = problem
formula_template["resultTemplate"] = resultTemplate
formula_template["checkResult"] = checkResult
if params.has_key("test"):
return generateFormula(formula_template)
saveFormulaTemplates()
return True
except:
return False
def saveFormulae():
try:
f = file("formulae.pickle", "w")
pickle.dump(formulae, f)
f.close()
except:
print sys.exc_info()[0]
def saveFormulaTemplates():
try:
f = file("templates.pickle", "w")
pickle.dump(formula_templates, f)
f.close()
except:
print sys.exc_info()[0]
class MathHelperRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
def do_GET(self):
if self.path == "/":
formula_templates_inuse = (i for i,v in enumerate(formula_templates) if not v["disabled"])
for i in formula_templates_inuse:
break
else:
self.send_response(302)
self.send_header("Location", "/templates")
self.send_header("Content-type", "text/html")
self.end_headers()
self.wfile.write("
Redirect Redirect ")
id = generateFormula()
self.send_response(302)
self.send_header("Location", "/formulae/%i" % id)
self.send_header("Content-type", "text/html")
self.end_headers()
self.wfile.write("Redirect Redirect " % id)
elif self.path.startswith("/formulae/"):
try:
id = None
incorrect = False
template_id = None
if self.path.endswith("/incorrect"):
incorrect = True
if (len(self.path.split("/")) == 5 or (incorrect and len(self.path.split("/")) == 6)) and self.path.split("/")[3] == "test":
path = self.path.split("/")
template_id = int(path[4])
id = int(path[2])
elif incorrect:
id = int(self.path[10:-10])
else:
id = int(self.path[10:])
formula = formulae[id]
except (ValueError, IndexError):
self.send_error(404)
return
self.writeFormula(id, formula, incorrect, template_id)
elif self.path == "/formulae":
self.writeFormulaList()
elif self.path == "/templates":
self.writeTemplateList()
elif self.path.startswith("/templates/") and self.path.endswith("/test"):
try:
id = int(self.path[11:-5])
formula_template = formula_templates[id]
except (ValueError, IndexError):
self.send_error(404)
return
formula_id = generateFormula(formula_template)
self.send_response(302)
self.send_header("Location", "/formulae/%i" % formula_id)
self.send_header("Content-type", "text/html")
self.end_headers()
self.wfile.write("Redirect Redirect " % formula_id)
elif self.path.startswith("/templates/"):
try:
edited = False
new = False
invalid = False
if self.path.endswith("/edited"):
id = int(self.path[11:-7])
edited = True
elif self.path.endswith("/new"):
id = int(self.path[11:-4])
new = True
elif self.path.endswith("/invalid"):
id = int(self.path[11:-8])
invalid = True
else:
id = int(self.path[11:])
formula_template = formula_templates[id]
except (ValueError, IndexError):
self.send_error(404)
return
self.writeTemplateEditPage(id, formula_template, new, edited)
elif self.path == "/style.css":
self.writeCSS()
elif self.path == "/quit":
threading.Thread(target=self.server.shutdown).start()
self.send_response(200)
self.send_header("Content-type", "text/plain")
self.end_headers()
self.wfile.write("Server shutting down.")
else:
self.send_error(404)
def do_POST(self):
clen = -1
try:
clen = int(self.headers["Content-length"])
except (ValueError, ValueError):
pass
params = urlparse.parse_qs(self.rfile.read(clen), True)
if self.path.startswith("/formulae/") and self.path.endswith("/check-result"):
try:
id = int(self.path[10:-13])
formula = formulae[id]
except (ValueError, IndexError):
self.send_error(404)
return
try:
if params.has_key("template_id"):
template_id = int(params["template_id"][0])
else:
template_id = None
except (IndexError):
self.send_error(404)
return
if checkResult(id, formula, params):
if template_id != None:
self.send_response(302)
self.send_header("Location", "/templates/%i" % template_id)
self.send_header("Content-type", "text/html")
self.end_headers()
self.wfile.write("Redirect Redirect " % template_id)
else:
self.writeCorrectResult()
else:
self.send_response(302)
self.send_header("Location", "/formulae/%i/%sincorrect" % (id, template_id != None and ("test/%i/" % template_id) or ""))
self.send_header("Content-type", "text/html")
self.end_headers()
self.wfile.write("Redirect Redirect " % (id, template_id != None and ("test/%i/" % template_id) or ""))
elif self.path == "/templates/new":
formula_templates.append(copy.copy(formula_templates[0]))
id = len(formula_templates)-1
formula_template = formula_templates[id]
formula_template["disabled"] = True
saveFormulaTemplates()
self.send_response(302)
self.send_header("Location", "/templates/%i/new" % id)
self.send_header("Content-type", "text/html")
self.end_headers()
self.wfile.write("Redirect Redirect " % id)
elif self.path.startswith("/templates/") and self.path.endswith("/edit"):
try:
id = int(self.path[11:-5])
formula_template = formula_templates[id]
except (ValueError, IndexError):
self.send_error(404)
return
r = editTemplate(id, formula_template, params)
if params.has_key("test") and r is not False:
self.send_response(302)
self.send_header("Location", "/formulae/%i/test/%s" % (r, id))
self.send_header("Content-type", "text/html")
self.end_headers()
self.wfile.write("Redirect Redirect " % (r, id))
elif r:
self.send_response(302)
self.send_header("Location", "/templates/%i/edited" % id)
self.send_header("Content-type", "text/html")
self.end_headers()
self.wfile.write("Redirect Redirect " % id)
else:
self.send_response(302)
self.send_header("Location", "/templates/%i/invalid" % id)
self.send_header("Content-type", "text/html")
self.end_headers()
self.wfile.write("Redirect Redirect " % id)
else:
self.send_error(404)
def writeFormula(self, id, formula, incorrect, template_id):
self.send_response(200)
self.send_header("Content-type", "text/html")
self.end_headers()
self.wfile.write("Formula #%i " % id)
self.wfile.write("Formula #%i View Templates - View Previous Problems ")
def writeFormulaList(self):
self.send_response(200)
self.send_header("Content-type", "text/html")
self.end_headers()
self.wfile.write("
Previous Problems ")
self.wfile.write("
Previous Problems ID Formula Inputs Problem Solution ")
for i,formula in enumerate(formulae):
self.wfile.write("%i %s %s %s %s " % (i, i, nl2br(cgi.escape(formula["template"]["formula"])), " ".join(tuple("%s = %s" % (cgi.escape(k), cgi.escape(str(v))) for k,v in formula["inputs"].items())), nl2br(cgi.escape(formula["template"]["problem"])), formula["solved"] and nl2br(cgi.escape(formula["solution"])) or "Not solved " % i))
self.wfile.write("
Back ")
def writeTemplateList(self):
self.send_response(200)
self.send_header("Content-type", "text/html")
self.end_headers()
self.wfile.write("
Formula Templates ")
self.wfile.write("
Formula Templates ID Name Input Generation Function Formula Problem Solution Template Solution Checking Function Disabled? Test ")
for i,formula_template in enumerate(formula_templates):
self.wfile.write("%i %s %s%s %s %s %s%s Go " % (i, i, i, cgi.escape(formula_template["name"]), nl2br(cgi.escape(formula_template["generateInputs"])), nl2br(cgi.escape(formula_template["formula"])), nl2br(cgi.escape(formula_template["problem"])), nl2br(cgi.escape(formula_template["resultTemplate"])), nl2br(cgi.escape(formula_template["checkResult"])), formula_template["disabled"] and "Yes" or "No", i))
self.wfile.write("
")
self.wfile.write("
")
self.wfile.write("
Back ")
def writeTemplateEditPage(self, id, formula_template, new=False, edited=True):
self.wfile.write("
Template #%i - %s " % (id, formula_template["name"]))
self.wfile.write("
")
def writeCorrectResult(self):
self.send_response(200)
self.send_header("Content-type", "text/html")
self.send_header("Refresh", "5;url=/")
self.end_headers()
self.wfile.write("
Correct Solution! ")
self.wfile.write("
Correct Solution! Click here to go on to the next question.
")
def writeCSS(self):
self.send_response(200)
self.send_header("Content-type", "text/css")
self.end_headers()
self.wfile.write("body{text-align:center;min-width:40%;} #wrapper{text-align:left;width:40%;margin-left:auto;margin-right:auto;} form p{width:40%;clear:left;margin:0;padding:5px 0 8px 0;padding-left:155px;border-top:1px dashed gray;min-height:2em;} form label{font-weight:bold;float:left;margin-left:-155px;width:150px;} form input[type=\"text\"]{width:180px;} form textarea{width:250px;height:150px;white-space:nowrap;overflow:auto;}")
try:
f = file("templates.pickle", "r")
formula_templates = pickle.load(f)
f.close()
except:
saveFormulaTemplates()
try:
f = file("formulae.pickle", "r")
formulae = pickle.load(f)
f.close()
except:
saveFormulae()
if __name__ == '__main__':
server_class = BaseHTTPServer.HTTPServer
httpd = server_class((HOST_NAME, PORT_NUMBER), MathHelperRequestHandler)
print time.asctime(), "Server Starts - %s:%s" % (HOST_NAME, PORT_NUMBER)
httpd.serve_forever()
httpd.server_close()
print time.asctime(), "Server Stops - %s:%s" % (HOST_NAME, PORT_NUMBER)