#!/usr/bin/env python3 # lunchuman.wsgi: a WSGI script for managing a simple lunch club # Copyright (C) 2012 Niels G. W. Serup # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the # License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public # License along with this program. If not, see # . ## Version: 0.1.0 ## Maintainer: Niels G. W. Serup ## Temporary website: http://datalosjen.metanohi.name/static/source/lunchuman import sys import os import bottle from bottle import route, abort, error, request, redirect import sqlite3 import threading import itertools import atexit import pickle import configparser import locale import traceback _preferred_encoding = 'utf-8' # locale.getpreferredencoding() # fail _absfile = os.path.abspath(__file__) _filedir = os.path.dirname(_absfile) _configfile_name = 'lunchuman.config' def start_bottle(): global application application = bottle.default_app() def block(): threading.Event().wait() with open(os.path.join(_filedir, '.dynamicsettings'), 'rb') as f: dynamic_settings = pickle.load(f) if dynamic_settings['status'] == 'stop': start_bottle() block() class LunchumanDatabase: def __init__(self, fname): self.dbfile = fname def load(self): self.conn = sqlite3.connect(self.dbfile) self.cursor = self.conn.cursor() self._create() def close(self): self.conn.close() def _create(self): self.cursor.execute(''' create table if not exists users ( id integer primary key autoincrement, name text, moneys int, foods int ) ''') self.conn.commit() def get_users(self): self.cursor.execute('select * from users order by moneys / (foods * 1.0) desc') return self.cursor.fetchall() def add_user(self, name): self.cursor.execute('insert into users values (NULL, ?, 0, 0)', (name,)) self.conn.commit() def update_user_data(self, *users): for id, new_name, moneys_incr, foods_incr in users: self.cursor.execute(''' update users set name=?, moneys=moneys + (?), foods=foods + (?) where id=? ''', (new_name, moneys_incr, foods_incr, id)) self.conn.commit() def add_settings_from_file(fname, settings): cfg = configparser.SafeConfigParser(settings) cfg.add_section('lunchuman') cfg.read(os.path.join(_filedir, fname)) return {name: value for name, value in cfg.items('lunchuman')} def dictpair(*ds): return {key: val for key, val in itertools.chain(*(d.items() for d in ds))} def read_file(locpath): with open(os.path.join(_filedir, locpath), 'rb') as f: return f.read().decode(_preferred_encoding) def get_seq_input(d, base_key): i = 0 xs = [] while True: try: xs.append(d['{}[{}]'.format(base_key, i)]) except KeyError: break i += 1 return xs settings = add_settings_from_file( _configfile_name, { 'name': 'Lunchuman', 'dbfile': 'lunchuman.sqlite', 'template': 'template.html', 'frontpage': 'frontpage.html' }) _template = read_file(settings['template']) _userdata_template = ''' {moneys} + {foods} + {goodwill} ''' _frontpage = _template.format(**dictpair(settings, { 'content': read_file(settings['frontpage']), 'headextra': "" })) db = LunchumanDatabase(os.path.join(_filedir, settings['dbfile'])) #atexit.register(db.close) db.load() @route('/') def frontpage(): users = db.get_users() userdata = '\n'.join(_userdata_template.format( id=users[i][0], name=repr(users[i][1]), moneys=users[i][2], foods=users[i][3], i=i, goodwill=('{:.2f}'.format( users[i][2] / users[i][3]) if users[i][3] > 0 else None)) for i in range(len(users))) return _frontpage.format(userdata=userdata) @route('/adduser', method='post') def add_user(): db.add_user(request.forms['name']) redirect('/') @route('/updateuserdata', method='post') def update_user_data(): users = (vals[1:] for vals in filter(lambda vals: vals[0] != vals[2] or (vals[3] != '0' and vals[3] != '') or (vals[4] != '0' and vals[4] != ''), zip(*(get_seq_input(request.forms, attr) for attr in ('orig_name', 'id', 'new_name', 'moneys_incr', 'foods_incr'))))) db.update_user_data(*users) redirect('/') start_bottle()