diff --git a/subsites/lunchuman-apache b/subsites/lunchuman-apache new file mode 100644 index 0000000..93b6eaf --- /dev/null +++ b/subsites/lunchuman-apache @@ -0,0 +1,30 @@ + + + ServerName datalosjen.metanohi.name + ServerAlias www.datalosjen.metanohi.name datalosjen.lcl + ServerAdmin ns@metanohi.name + + DocumentRoot /home/niels/www/meta/subsites/lunchuman + + Alias /robots.txt /home/niels/www/meta/subsites/lunchuman/robots.txt + Alias /favicon.ico /home/niels/www/meta/subsites/lunchuman/favicon.ico + Alias /favicon.png /home/niels/www/meta/subsites/lunchuman/favicon.png + Alias /static /home/niels/www/meta/subsites/lunchuman/static + Alias /lunchuman.wsgi /home/niels/www/meta/subsites/films/lunchuman.wsgi + Alias /apache-config-template /home/niels/www/meta/subsites/lunchuman/apache-config-template + + + Order allow,deny + Allow from all + + + WSGIDaemonProcess lunchuman.metanohi.name processes=1 threads=1 display-name=%{GROUP} + WSGIProcessGroup lunchuman.metanohi.name + + WSGIScriptAlias / /home/niels/www/meta/subsites/lunchuman/lunchuman.wsgi + + LogLevel warn + ErrorLog /var/log/apache2/lunchuman.metanohi.name-error.log + CustomLog /var/log/apache2/lunchuman.metanohi.name-access.log combined + + \ No newline at end of file diff --git a/subsites/lunchuman/.gitignore b/subsites/lunchuman/.gitignore new file mode 100644 index 0000000..b18461c --- /dev/null +++ b/subsites/lunchuman/.gitignore @@ -0,0 +1,3 @@ +/.dynamicsettings +/.old +/lunchuman.sqlite \ No newline at end of file diff --git a/subsites/lunchuman/apache-config-template b/subsites/lunchuman/apache-config-template new file mode 100644 index 0000000..c434db8 --- /dev/null +++ b/subsites/lunchuman/apache-config-template @@ -0,0 +1,30 @@ + + + ServerName lunchuman.example.org + ServerAlias www.lunchuman.example.org + ServerAdmin webmaster@example.org + + DocumentRoot /path/to/dir/of/lunchuman + + Alias /robots.txt /path/to/dir/of/lunchuman/robots.txt + Alias /favicon.ico /path/to/dir/of/lunchuman/favicon.ico + Alias /favicon.png /path/to/dir/of/lunchuman/favicon.png + Alias /static /path/to/dir/of/lunchuman/static + Alias /lunchuman.wsgi /path/to/dir/of/lunchuman/lunchuman.wsgi + Alias /apache-config-template /path/to/dir/of/lunchuman/apache-config-template + + + Order allow,deny + Allow from all + + + WSGIDaemonProcess lunchuman.example.org processes=2 threads=15 display-name=%{GROUP} + WSGIProcessGroup lunchuman.example.org + + WSGIScriptAlias / /path/to/dir/of/lunchuman/lunchuman.wsgi + + LogLevel warn + ErrorLog /var/log/apache2/lunchuman.example.org-error.log + CustomLog /var/log/apache2/lunchuman.example.org-access.log combined + + diff --git a/subsites/lunchuman/frontpage.html b/subsites/lunchuman/frontpage.html new file mode 100644 index 0000000..64bfafb --- /dev/null +++ b/subsites/lunchuman/frontpage.html @@ -0,0 +1,37 @@ +

Add user

+ +
+

Name:

+ +
+ + +

Update user data

+ +
+ + + + + + + + + + + {userdata} + +
NameMoneysFoodsGoodwill (moneys / foods)
+ +
+ + +

FAQ

+ +

Question: The words "foods" and "moneys" are incorrect. Why + do you use them?

+

Answer: In the domain of this lunch club, they are correct.

+ + +Tihs is not a typo. + diff --git a/subsites/lunchuman/lunchuman.config b/subsites/lunchuman/lunchuman.config new file mode 100644 index 0000000..54fa3d3 --- /dev/null +++ b/subsites/lunchuman/lunchuman.config @@ -0,0 +1,4 @@ +[lunchuman] + +name = Datalosjen + diff --git a/subsites/lunchuman/lunchuman.wsgi b/subsites/lunchuman/lunchuman.wsgi new file mode 100644 index 0000000..4478c39 --- /dev/null +++ b/subsites/lunchuman/lunchuman.wsgi @@ -0,0 +1,183 @@ +#!/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 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() diff --git a/subsites/lunchuman/manage b/subsites/lunchuman/manage new file mode 100755 index 0000000..e000089 --- /dev/null +++ b/subsites/lunchuman/manage @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 + +import sys +import os +import pickle + +_absfile = os.path.abspath(__file__) +_filedir = os.path.dirname(_absfile) +dfile = os.path.join(_filedir, '.dynamicsettings') + +def write(data): + with open(dfile, 'wb') as f: + pickle.dump(data, f) + +def touch(fname): + if os.path.exists(fname): + return os.utime(fname, None) + else: + with open(path, 'w'): + pass + +def _traverse(xs): + for x in xs: + pass + +def act(d): + try: + d[sys.argv[1]]() + except (IndexError, KeyError): + print('unknown command', file=sys.stderr) + print_help() + +def print_help(): + print(''' +Run one of: + stop + start + restart +''') + +if __name__ == '__main__': + act({ + 'stop': lambda: write({'status': 'stop'}), + 'start': lambda: write({'status': 'running'}), + 'restart': lambda: _traverse( + f() for f in (lambda: write({'status': 'running'}), + lambda: touch(dfile))), + 'help': print_help, '-h': print_help, '--help': print_help + }) diff --git a/subsites/lunchuman/robots.txt b/subsites/lunchuman/robots.txt new file mode 100644 index 0000000..7d329b1 --- /dev/null +++ b/subsites/lunchuman/robots.txt @@ -0,0 +1 @@ +User-agent: * diff --git a/subsites/lunchuman/static/front.js b/subsites/lunchuman/static/front.js new file mode 100644 index 0000000..be0ea91 --- /dev/null +++ b/subsites/lunchuman/static/front.js @@ -0,0 +1,19 @@ +create_listener = function(elem) { + var listener = function() { + elem.value = ''; + elem.removeEventListener('focus', listener, false); + }; + return listener; +} + +prepare_delete_onfocus = function() { + var elems, elemI, elem, listener; + elems = document.getElementsByClassName('deleteonfocus'); + for (elemI in elems) { + elem = elems[elemI]; + elem.addEventListener('focus', create_listener(elem), false); + } +} + +window.onload = function(event) {prepare_delete_onfocus();} + diff --git a/subsites/lunchuman/static/lunchuman b/subsites/lunchuman/static/lunchuman new file mode 120000 index 0000000..b8df013 --- /dev/null +++ b/subsites/lunchuman/static/lunchuman @@ -0,0 +1 @@ +/home/niels/www/meta/subsites/lunchuman \ No newline at end of file diff --git a/subsites/lunchuman/static/screen.css b/subsites/lunchuman/static/screen.css new file mode 100644 index 0000000..34e8ec0 --- /dev/null +++ b/subsites/lunchuman/static/screen.css @@ -0,0 +1,57 @@ +#header +{ + background-color: pink; + color: green; + text-decoration: blink; + text-align: right; +} + +#content +{ + +} + +#footer +{ + border-top: 1px black solid; + margin-top: 20px; +} + + + +table { + margin: 5px auto; + border-collapse: collapse; +} + +table, thead, tbody, tfoot, td, th { + border-style: inset; + border-color: black; +} + +td, th { + margin: 0 5px; + padding: 1px 3px; + border-width: 0 2px 0 0; +} + +td:last-child, th:last-child { + border-width: 0; +} + +table, tbody { + border-width: 0; +} + +thead { + border-width: 0 0 2px 0; +} + +tfoot { + border-width: 2px 0 0 0; +} + +thead, tfoot { + font-weight: bold; +} + diff --git a/subsites/lunchuman/static/source b/subsites/lunchuman/static/source new file mode 120000 index 0000000..945c9b4 --- /dev/null +++ b/subsites/lunchuman/static/source @@ -0,0 +1 @@ +. \ No newline at end of file diff --git a/subsites/lunchuman/template.html b/subsites/lunchuman/template.html new file mode 100644 index 0000000..fe713d0 --- /dev/null +++ b/subsites/lunchuman/template.html @@ -0,0 +1,34 @@ + + + + + {name} + + + + + + {headextra} + + + + +
+ {content} +
+ + + + +