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("RedirectRedirect") 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("RedirectRedirect" % 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("RedirectRedirect" % 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("RedirectRedirect" % 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("RedirectRedirect" % (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("RedirectRedirect" % 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("RedirectRedirect" % (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("RedirectRedirect" % 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("RedirectRedirect" % 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

" % (id, id)) if incorrect: self.wfile.write("

Incorrect solution.

") self.wfile.write("

%s

" % nl2br(cgi.escape(formula["template"]["formula"]))) self.wfile.write("

%s

" % "
".join(tuple("%s = %s" % (cgi.escape(k), cgi.escape(str(v))) for k,v in formula["inputs"].items()))) self.wfile.write("

%s

" % nl2br(cgi.escape(formula["template"]["problem"]))) def sanitize(o): return cgi.escape(o.group(0)) def _inc(): i = 0 while True: yield i i += 1 inc = _inc() def makeInput(o): i = inc.next() return "" % (o.group(1), inc.next()) answerform = formula["template"]["resultTemplate"] answerform = re.sub(r'(.*?)(?:%[a-zA-Z]%|$)', sanitize, formula["template"]["resultTemplate"]) answerform = re.sub(r'%([a-zA-Z]+)%', makeInput, answerform) self.wfile.write("

%s

" % nl2br(answerform)) for k,v in formula["inputs"].iteritems(): self.wfile.write("" % (k, inc.next(), str(v))) if template_id != None: self.wfile.write("" % template_id) self.wfile.write("

- Skip

") self.wfile.write("
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

") for i,formula in enumerate(formulae): self.wfile.write("" % (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("
IDFormulaInputsProblemSolution
%i%s%s%s%s
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

") for i,formula_template in enumerate(formula_templates): self.wfile.write("" % (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("
IDNameInput Generation FunctionFormulaProblemSolution TemplateSolution Checking FunctionDisabled?Test
%i%s%s%s%s%s%s%sGo
") 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("

Template #%i - %s

" % (id, formula_template["name"], id)) if new: self.wfile.write("

New template.

") if edited: self.wfile.write("

Successfully edited.

") self.wfile.write("

" % cgi.escape(formula_template["name"])) self.wfile.write("

" % cgi.escape(formula_template["generateInputs"])) self.wfile.write("

" % cgi.escape(formula_template["formula"])) self.wfile.write("

" % cgi.escape(formula_template["problem"])) self.wfile.write("

" % cgi.escape(formula_template["resultTemplate"])) self.wfile.write("

" % cgi.escape(formula_template["checkResult"])) self.wfile.write("

" % (formula_template["disabled"] and "checked" or "")) self.wfile.write("

" % (new and "Create" or "Edit")) self.wfile.write("
Back
") 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)