# HG changeset patch # User k0s # Date 1252350543 14400 # Node ID 1eea6356d2e53f6895e35985fddc80d73d5df425 initial import of webcalc diff -r 000000000000 -r 1eea6356d2e5 setup.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/setup.py Mon Sep 07 15:09:03 2009 -0400 @@ -0,0 +1,36 @@ +from setuptools import setup, find_packages + +version = "0.1" + +setup(name='webcalc', + version=version, + description="web-based calculator", + long_description=""" +""", + classifiers=[], # Get strings from http://www.python.org/pypi?%3Aaction=list_classifiers + author='Jeff Hammel', + author_email='k0scist@gmail.com', + url='', + license="GPL", + packages=find_packages(exclude=['ez_setup', 'examples', 'tests']), + include_package_data=True, + zip_safe=False, + install_requires=[ + # -*- Extra requirements: -*- + 'WebOb', + 'Paste', + 'PasteScript', + 'genshi', + 'simplejson' + ], + entry_points=""" + # -*- Entry points: -*- + [paste.app_factory] + main = webcalc.factory:factory + + [webcalc.formatters] + text/plain = webcalc.formatters:CSVformat + application/json = webcalc.formatters:JSONformat + """, + ) + diff -r 000000000000 -r 1eea6356d2e5 webcalc.ini --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/webcalc.ini Mon Sep 07 15:09:03 2009 -0400 @@ -0,0 +1,21 @@ +#!/usr/bin/env paster + +[DEFAULT] +debug = true +email_to = k0scist@gmail.com +smtp_server = localhost +error_email_from = paste@localhost + +[server:main] +use = egg:Paste#http +host = 0.0.0.0 +port = 5151 + +[composite:main] +use = egg:Paste#urlmap +/ = webcalc + +set debug = false + +[app:webcalc] +paste.app_factory = webcalc.factory:factory diff -r 000000000000 -r 1eea6356d2e5 webcalc/__init__.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/webcalc/__init__.py Mon Sep 07 15:09:03 2009 -0400 @@ -0,0 +1,1 @@ +# diff -r 000000000000 -r 1eea6356d2e5 webcalc/factory.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/webcalc/factory.py Mon Sep 07 15:09:03 2009 -0400 @@ -0,0 +1,12 @@ +from webcalc import WebcalcView +from paste.httpexceptions import HTTPExceptionHandler + +def factory(global_conf, **app_conf): + """create a webob view and wrap it in middleware""" + keystr = 'webcalc.' + args = dict([(key.split(keystr, 1)[-1], value) + for key, value in app_conf.items() + if key.startswith(keystr) ]) + app = WebcalcView(**args) + return HTTPExceptionHandler(app) + diff -r 000000000000 -r 1eea6356d2e5 webcalc/formatters.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/webcalc/formatters.py Mon Sep 07 15:09:03 2009 -0400 @@ -0,0 +1,20 @@ +from StringIO import StringIO + +def CSVformat(values): + if not values: + return '' + keys = sorted([key for key in values[0].keys() + if key != 'result']) + keys.append('result') + + buffer = StringIO() + if len(keys) > 1: + print >> buffer, ','.join(keys) + for value in values: + print >> buffer, ','.join([str(value[key]) + for key in keys]) + return buffer.getvalue() + +def JSONformat(values): + import simplejson + return simplejson.dumps({'values': values}) diff -r 000000000000 -r 1eea6356d2e5 webcalc/templates/index.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/webcalc/templates/index.html Mon Sep 07 15:09:03 2009 -0400 @@ -0,0 +1,9 @@ + + + +Hello ${subject}! + + diff -r 000000000000 -r 1eea6356d2e5 webcalc/webcalc.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/webcalc/webcalc.py Mon Sep 07 15:09:03 2009 -0400 @@ -0,0 +1,144 @@ +""" +webcalc: a view with webob +""" + +import math +from pkg_resources import iter_entry_points +from pkg_resources import resource_filename +from webob import Request, Response, exc + +class WebcalcView(object): + + ### class level variables + defaults = { 'formatter': 'text/plain' } + + def __init__(self, **kw): + for key in self.defaults: + setattr(self, key, kw.get(key, self.defaults[key])) + self.formatters = {} + for entry_point in iter_entry_points('webcalc.formatters'): + try: + formatter = entry_point.load() + except: + continue + self.formatters[entry_point.name] = formatter + + assert self.formatter in self.formatters + + self.response_functions = { 'GET': self.get, + 'POST': self.get, + } + + ### methods dealing with HTTP + def __call__(self, environ, start_response): + request = Request(environ) + res = self.make_response(request) + return res(environ, start_response) + + def make_response(self, request): + return self.response_functions.get(request.method, self.error)(request) + + def get_response(self, text, content_type='text/html'): + res = Response(content_type=content_type, body=text) + return res + + def get(self, request): + """ + return response to a GET requst + """ + + + equation = request.path_info.strip('/').strip() + if not equation: + return self.get_response('
') + + + # limit scope of execution + forbidden = set([';', '=', 'import', 'from', 'with']) + forbidden.update(dir(__builtins__)) + assert not [ i for i in forbidden + if i in equation ] + math_globals = dict([(i, getattr(math, i)) + for i in dir(math) + if not i.startswith('_')]) + + # get the range of variables + variables = [] + assert 'result' not in request.GET + for name, value in request.GET.items(): + if name == 'format': + continue + values = self.values(value) + if variables: + if len(values) == 1: + for var in variables: + var[name] = values[0] + else: + old_variables = [ i.copy() for i in variables[:] ] + variables = [] + for val in values: + row = [ i.copy() for i in old_variables ] + for item in row: + item[name] = val + variables.extend(row) + else: + variables = [ { name: val } + for val in values ] + + # evaluate the equation + if variables: + for var in variables: + result = float(eval(equation, math_globals, var)) + var['result'] = result + else: + result = float(eval(equation, math_globals, {})) + variables = [{'result': result}] + + # format the result + format = request.params.get('format') or request.content_type or self.formatter + formatter = self.formatters.get(format, self.formatters[self.formatter]) + return self.get_response(formatter(variables), format) + + def post(self, request): + """ + return response to a POST request + """ + import pdb; pdb.set_trace() + + return exc.HTTPOk("Ok") + + def error(self, request): + """deal with non-supported methods""" + return exc.HTTPMethodNotAllowed("Only %r operations are allowed" % self.response_functions.keys()) + + + def values(self, string): + """ + >>> values('1,2,3,4') + [1,2,3,4] + >>> values('1..0.2..2') + [1,1.2,1.4,1.6,1.8,2] + """ + retval = [] + tokens = [i.strip() for i in string.split(',') + if i.strip()] + for token in tokens: + try: + value = float(token) + retval.append(value) + continue + except ValueError: + pass + start, step, end = token.split('..') + start = float(start) + step = float(step) + end = float(end) + + values = [ start ] + npoints = (end - start)/step + npoints = int(npoints) + values.extend([i*step + start + for i in range(1, npoints)]) + values.append(end) + retval.extend(values) + return retval