Merge branch 'morejson'
This commit is contained in:
commit
551c8641d3
|
@ -0,0 +1,5 @@
|
||||||
|
[client-barcode]
|
||||||
|
SERVER_URL = https://example.org:443/api2/
|
||||||
|
SERVER_USER = user
|
||||||
|
SERVER_PASSWORD = password
|
||||||
|
#CLIENT_DEBUG = 1
|
|
@ -8,6 +8,7 @@ from __future__ import print_function
|
||||||
import freitagslib.network as net
|
import freitagslib.network as net
|
||||||
from freitagslib.commands import BuyCommand, DepositCommand
|
from freitagslib.commands import BuyCommand, DepositCommand
|
||||||
from freitagslib.encoding import asciify
|
from freitagslib.encoding import asciify
|
||||||
|
from freitagslib.settings import settings, Settings
|
||||||
|
|
||||||
import colorama
|
import colorama
|
||||||
from colorama import Fore, Style
|
from colorama import Fore, Style
|
||||||
|
@ -519,6 +520,10 @@ def read_line(f, timeout, timeout_func):
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
colorama.init()
|
colorama.init()
|
||||||
|
|
||||||
|
if not settings.load("freitagskasse.conf"):
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
status = Status()
|
status = Status()
|
||||||
global scroll_line1,scroll_line2
|
global scroll_line1,scroll_line2
|
||||||
global myDisplay
|
global myDisplay
|
||||||
|
|
|
@ -53,7 +53,7 @@ class BuyCommand(object):
|
||||||
raise ValueError("BuyCommand in illegal state")
|
raise ValueError("BuyCommand in illegal state")
|
||||||
|
|
||||||
def run(self, user_name):
|
def run(self, user_name):
|
||||||
net.buy_item(self._item.id, what_about_it=self._what_to_buy(), user_name=user_name)
|
raise NotImplementedError('BuyCommand.run(user_name)')
|
||||||
|
|
||||||
def item_name(self):
|
def item_name(self):
|
||||||
return self._item.name
|
return self._item.name
|
||||||
|
|
|
@ -18,39 +18,43 @@ if __name__ == '__main__':
|
||||||
sys.path.append('.')
|
sys.path.append('.')
|
||||||
from freitagslib.item import Item
|
from freitagslib.item import Item
|
||||||
from freitagslib.tools import require_type
|
from freitagslib.tools import require_type
|
||||||
|
from freitagslib.settings import settings, Settings
|
||||||
|
|
||||||
|
|
||||||
DEPOSIT_CASH = 1
|
DEPOSIT_CASH = 1
|
||||||
DEPOSIT_BANK = 2
|
DEPOSIT_BANK = 2
|
||||||
|
|
||||||
|
|
||||||
_BASE_URL = 'https://k4ever.freitagsrunde.org:443/api2/'
|
|
||||||
|
|
||||||
_auth = base64.encodestring('%s:%s' % (os.environ['BARCODE_PLUGIN_USER'], os.environ['BARCODE_PLUGIN_PASS'])).rstrip()
|
|
||||||
_headers = {'Authorization':'Basic %s' % _auth}
|
|
||||||
|
|
||||||
ITEM_ONLY, DEPOSIT_ONLY, ITEM_AND_DEPOSIT = range(3)
|
ITEM_ONLY, DEPOSIT_ONLY, ITEM_AND_DEPOSIT = range(3)
|
||||||
|
|
||||||
|
|
||||||
if bool(os.environ.get('BARCODE_PLUGIN_DEBUG', False)):
|
def _urlopen(*args):
|
||||||
|
if settings.CLIENT_DEBUG:
|
||||||
_h = urllib2.HTTPHandler(debuglevel=1)
|
_h = urllib2.HTTPHandler(debuglevel=1)
|
||||||
_opener = urllib2.build_opener(_h)
|
_opener = urllib2.build_opener(_h)
|
||||||
_urlopen = _opener.open
|
_func = _opener.open
|
||||||
else:
|
else:
|
||||||
_urlopen = urllib2.urlopen
|
_func = urllib2.urlopen
|
||||||
|
return _func(*args)
|
||||||
|
|
||||||
|
|
||||||
|
def _get_default_headers():
|
||||||
|
_auth = base64.encodestring('%s:%s' % (settings.SERVER_USER, settings.SERVER_PASSWORD)).rstrip()
|
||||||
|
_headers = {'Authorization':'Basic %s' % _auth}
|
||||||
|
return _headers
|
||||||
|
|
||||||
|
|
||||||
def _request(api_rel_url, data, method, headers=None):
|
def _request(api_rel_url, data, method, headers=None):
|
||||||
url = _BASE_URL + api_rel_url
|
url = settings.SERVER_URL + api_rel_url
|
||||||
|
|
||||||
if isinstance(data, dict):
|
if isinstance(data, dict):
|
||||||
data = urllib.urlencode(data)
|
data = urllib.urlencode(data)
|
||||||
|
|
||||||
# Add default headers
|
# Add default headers
|
||||||
if headers is None:
|
if headers is None:
|
||||||
final_headers = _headers
|
final_headers = _get_default_headers()
|
||||||
else:
|
else:
|
||||||
final_headers = copy.copy(_headers)
|
final_headers = copy.copy(_get_default_headers())
|
||||||
final_headers.update(headers)
|
final_headers.update(headers)
|
||||||
|
|
||||||
if method == 'GET':
|
if method == 'GET':
|
||||||
|
@ -71,16 +75,6 @@ def get_user_name_from_auth_blob(authblob):
|
||||||
return d['username']
|
return d['username']
|
||||||
|
|
||||||
|
|
||||||
def buy_item(item_id, what_about_it, user_name):
|
|
||||||
require_type(int, item_id)
|
|
||||||
assert(what_about_it in (ITEM_ONLY, DEPOSIT_ONLY, ITEM_AND_DEPOSIT))
|
|
||||||
|
|
||||||
content = _request('buyable/item/' + urllib.quote(str(item_id)),
|
|
||||||
{'user':user_name, 'deposit':what_about_it, 'amount':1}, method='POST')
|
|
||||||
if content != 'Created':
|
|
||||||
raise ValueError('Server says "%s"' % content)
|
|
||||||
|
|
||||||
|
|
||||||
def bulk_buy(buycommands, user_name):
|
def bulk_buy(buycommands, user_name):
|
||||||
item_ids = tuple(e.item_id() for e in buycommands if e.includes_commodity())
|
item_ids = tuple(e.item_id() for e in buycommands if e.includes_commodity())
|
||||||
deposit_ids = tuple(e.item_id() for e in buycommands if e.includes_deposit())
|
deposit_ids = tuple(e.item_id() for e in buycommands if e.includes_deposit())
|
||||||
|
@ -99,7 +93,8 @@ def bulk_buy(buycommands, user_name):
|
||||||
'Content-Length':len(data)
|
'Content-Length':len(data)
|
||||||
}
|
}
|
||||||
content = _request('buyable/item/bulkbuy/', data, 'POST', headers=headers)
|
content = _request('buyable/item/bulkbuy/', data, 'POST', headers=headers)
|
||||||
if content != 'Created':
|
d = json.loads(content)
|
||||||
|
if not d['success']:
|
||||||
raise ValueError('Server says "%s"' % content)
|
raise ValueError('Server says "%s"' % content)
|
||||||
|
|
||||||
|
|
||||||
|
@ -123,15 +118,17 @@ def deposit(amount, transaction_type, user_name):
|
||||||
|
|
||||||
content = _request('account/transactions/transact/',
|
content = _request('account/transactions/transact/',
|
||||||
{'user':user_name, 'amount':amount, 'type':transaction_type}, method='POST')
|
{'user':user_name, 'amount':amount, 'type':transaction_type}, method='POST')
|
||||||
if content != 'OK':
|
d = json.loads(content)
|
||||||
|
if not d['success']:
|
||||||
raise ValueError('Server says "%s"' % content)
|
raise ValueError('Server says "%s"' % content)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
user_name = get_user_name_from_auth_blob(os.environ['BARCODE_PLUGIN_TEST_AUTH_BLOB'])
|
if not settings.load('freitagskasse.conf', (('TEST_AUTH_BLOB', str, None), )):
|
||||||
print('User name: ' + user_name)
|
sys.exit(1)
|
||||||
|
|
||||||
buy_item(1, True, user_name)
|
user_name = get_user_name_from_auth_blob(settings.TEST_AUTH_BLOB)
|
||||||
|
print('User name: ' + user_name)
|
||||||
|
|
||||||
balance = get_balance(user_name)
|
balance = get_balance(user_name)
|
||||||
print('Balance is %f, type is "%s"' % (balance, type(balance).__name__))
|
print('Balance is %f, type is "%s"' % (balance, type(balance).__name__))
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
#! /usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# Copyright (C) 2011 Sebastian Pipping <sebastian@pipping.org>
|
||||||
|
# Licensed under GPL v3 or later
|
||||||
|
|
||||||
|
from __future__ import print_function
|
||||||
|
import sys
|
||||||
|
import ConfigParser
|
||||||
|
|
||||||
|
|
||||||
|
_SECTION = 'client-barcode'
|
||||||
|
|
||||||
|
_KEYS = (
|
||||||
|
# Key, type, default
|
||||||
|
('CLIENT_DEBUG', bool, False), # Should go first to enable debugging as early as possible
|
||||||
|
('SERVER_URL', str, None),
|
||||||
|
('SERVER_USER', str, None),
|
||||||
|
('SERVER_PASSWORD', str, None),
|
||||||
|
)
|
||||||
|
|
||||||
|
_DEFAULTS = dict([(key, str(default))
|
||||||
|
for (key, _type, default)
|
||||||
|
in _KEYS
|
||||||
|
if default is not None])
|
||||||
|
|
||||||
|
|
||||||
|
class Settings:
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def load(self, filename, _additional_keys=None):
|
||||||
|
additional_keys = () if _additional_keys is None else _additional_keys
|
||||||
|
|
||||||
|
cp = ConfigParser.ConfigParser(_DEFAULTS)
|
||||||
|
if not cp.read(filename):
|
||||||
|
print('FATAL: Config file "%s" could not be parsed.' % (filename), file=sys.stderr)
|
||||||
|
return False
|
||||||
|
|
||||||
|
valid = True
|
||||||
|
for key, _type, dummy in _KEYS + additional_keys:
|
||||||
|
try:
|
||||||
|
if _type is bool:
|
||||||
|
value = cp.getboolean(_SECTION, key)
|
||||||
|
else:
|
||||||
|
value = cp.get(_SECTION, key)
|
||||||
|
except ValueError:
|
||||||
|
print('ERROR: Key "%s" in config file "%s" must be of type "%s".' \
|
||||||
|
% (key, filename, _type.__name__), file=sys.stderr)
|
||||||
|
valid = False
|
||||||
|
except ConfigParser.NoOptionError:
|
||||||
|
print('ERROR: Config file "%s" misses required key "%s.%s".' \
|
||||||
|
% (filename, _SECTION, key), file=sys.stderr)
|
||||||
|
valid = False
|
||||||
|
else:
|
||||||
|
setattr(self, key, value)
|
||||||
|
if self.CLIENT_DEBUG:
|
||||||
|
print('DEBUG: CONFIG: %s.%s=%s("%s")' % (_SECTION, key, type(value).__name__, value))
|
||||||
|
|
||||||
|
return valid
|
||||||
|
|
||||||
|
|
||||||
|
settings = Settings()
|
|
@ -116,7 +116,7 @@ class BuyableItemHandler(BaseHandler):
|
||||||
p.save(saveOrder=False)
|
p.save(saveOrder=False)
|
||||||
order.save()
|
order.save()
|
||||||
|
|
||||||
return {'success': True, 'balance': request.user.getProfile().balance}
|
return {'success': True, 'balance': request.user.get_profile().balance}
|
||||||
|
|
||||||
def bulkBuy(self, request):
|
def bulkBuy(self, request):
|
||||||
"""Buy a :class:`Buyable <buyable.models.Buyable>` item.
|
"""Buy a :class:`Buyable <buyable.models.Buyable>` item.
|
||||||
|
@ -179,7 +179,7 @@ class BuyableItemHandler(BaseHandler):
|
||||||
p.save(saveOrder=False)
|
p.save(saveOrder=False)
|
||||||
order.save()
|
order.save()
|
||||||
|
|
||||||
return {'success': True, 'balance': request.user.getProfile().balance}
|
return {'success': True, 'balance': request.user.get_profile().balance}
|
||||||
|
|
||||||
|
|
||||||
class BuyableTypeHandler(BaseHandler):
|
class BuyableTypeHandler(BaseHandler):
|
||||||
|
@ -290,7 +290,7 @@ class TransactionTransactHandler(BaseHandler):
|
||||||
return getError(rc.BAD_REQUEST, "Your TransactionType could not be found")
|
return getError(rc.BAD_REQUEST, "Your TransactionType could not be found")
|
||||||
trans = Transaction(user=request.user, transactionType=tType, amount=amount, checked=not tType.needsCheck)
|
trans = Transaction(user=request.user, transactionType=tType, amount=amount, checked=not tType.needsCheck)
|
||||||
trans.save()
|
trans.save()
|
||||||
return {'success': True, 'balance': request.user.getProfile().balance}
|
return {'success': True, 'balance': request.user.get_profile().balance}
|
||||||
|
|
||||||
class TransactionTypeHandler(BaseHandler):
|
class TransactionTypeHandler(BaseHandler):
|
||||||
"""Handler for :class:`Transaction Types <transaction.models.TransactionType>`
|
"""Handler for :class:`Transaction Types <transaction.models.TransactionType>`
|
||||||
|
@ -348,7 +348,7 @@ class TransactionVirtualHandler(BaseHandler):
|
||||||
return getError(rc.BAD_REQUEST, "The recipient user does not exist.")
|
return getError(rc.BAD_REQUEST, "The recipient user does not exist.")
|
||||||
trans = VirtualTransaction(user=request.user, recipient=recipient, amount=amount, comment=comment)
|
trans = VirtualTransaction(user=request.user, recipient=recipient, amount=amount, comment=comment)
|
||||||
trans.save()
|
trans.save()
|
||||||
return {'success': True, 'balance': request.user.getProfile().balance}
|
return {'success': True, 'balance': request.user.get_profile().balance}
|
||||||
|
|
||||||
class AccountBalanceHandler(BaseHandler):
|
class AccountBalanceHandler(BaseHandler):
|
||||||
"""Handler for the user's account balance"""
|
"""Handler for the user's account balance"""
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% comment %}
|
||||||
|
# This file is part of k4ever, a point-of-sale system
|
||||||
|
# Contact............ <k4ever@lists.someserver.de>
|
||||||
|
# Website............ http://k4ever.someserver.de/
|
||||||
|
# Bug tracker........ http://k4ever.someserver.de/report
|
||||||
|
#
|
||||||
|
# Licensed under GNU Affero General Public License v3 or later
|
||||||
|
{% endcomment %}
|
||||||
|
|
||||||
|
{% block "content" %}
|
||||||
|
<h2>Whoops! Wir glauben du gehörst hier nicht hin...</h2>
|
||||||
|
<p>...zumindest sagt uns das dein Ausweis. Wie dem auch sei: Bis hierhin und nicht weiter</p>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,17 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% comment %}
|
||||||
|
# This file is part of k4ever, a point-of-sale system
|
||||||
|
# Contact............ <k4ever@lists.someserver.de>
|
||||||
|
# Website............ http://k4ever.someserver.de/
|
||||||
|
# Bug tracker........ http://k4ever.someserver.de/report
|
||||||
|
#
|
||||||
|
# Licensed under GNU Affero General Public License v3 or later
|
||||||
|
{% endcomment %}
|
||||||
|
|
||||||
|
{% block "content" %}
|
||||||
|
<h2>Whoops! Da ist was schiefgegangen</h2>
|
||||||
|
<p>Die Seite, die du angefordert hast, konnte nicht gefunden werden.</p>
|
||||||
|
|
||||||
|
<p>Zahllose Ponies sind bereits auf der Suche nach ihr.</p>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,31 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% comment %}
|
||||||
|
# This file is part of k4ever, a point-of-sale system
|
||||||
|
# Contact............ <k4ever@lists.someserver.de>
|
||||||
|
# Website............ http://k4ever.someserver.de/
|
||||||
|
# Bug tracker........ http://k4ever.someserver.de/report
|
||||||
|
#
|
||||||
|
# Licensed under GNU Affero General Public License v3 or later
|
||||||
|
{% endcomment %}
|
||||||
|
|
||||||
|
{% block head %}
|
||||||
|
<link rel="stylesheet" media="screen" type="text/css"
|
||||||
|
href="/media/css/style.css" />
|
||||||
|
{% block extrastyle %}{% endblock %}
|
||||||
|
<script type="text/javascript">
|
||||||
|
var MEDIA_URL = "/media/";
|
||||||
|
</script>
|
||||||
|
<script type='text/javascript' src="/media/js/jquery-1.5.1.js"></script>
|
||||||
|
<script type='text/javascript' src="/media/js/jquery-ui-1.8.10.custom.min.js"></script>
|
||||||
|
<script type='text/javascript' src="/media/js/jquery.ui.autocomplete.html.js"></script>
|
||||||
|
<script type='text/javascript' src="/media/js/jquery.gritter.min.js"></script>
|
||||||
|
<script type='text/javascript' src="/media/js/k4ever.js"></script>
|
||||||
|
{% endblock head %}
|
||||||
|
|
||||||
|
{% block "content" %}
|
||||||
|
<h2>Whoops! Da ist was schiefgegangen</h2>
|
||||||
|
<p>Bei der Bearbeitung deiner Anfrage ist ein Fehler aufgetreten für den du nichts kannst. Sorry!</p>
|
||||||
|
|
||||||
|
<p>Bitte nutze den Zurück-Button deines Browsers um zurückzufahren.</p>
|
||||||
|
{% endblock %}
|
|
@ -11,11 +11,12 @@
|
||||||
<html {% if user.is_authenticated %}class="loggedIn"{% endif %}>
|
<html {% if user.is_authenticated %}class="loggedIn"{% endif %}>
|
||||||
<head>
|
<head>
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
||||||
|
{% block head %}
|
||||||
<link rel="stylesheet" media="screen" type="text/css"
|
<link rel="stylesheet" media="screen" type="text/css"
|
||||||
href="{{ MEDIA_URL }}css/style.css" />
|
href="{{ MEDIA_URL }}css/style.css" />
|
||||||
{% block extrastyle %}{% endblock %}
|
{% block extrastyle %}{% endblock %}
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var MEDIA_URL = "{{MEDIA_URL}}";
|
var MEDIA_URL = "{{ MEDIA_URL }}";
|
||||||
</script>
|
</script>
|
||||||
<script type='text/javascript' src="{{ MEDIA_URL }}js/jquery-1.5.1.js"></script>
|
<script type='text/javascript' src="{{ MEDIA_URL }}js/jquery-1.5.1.js"></script>
|
||||||
<script type='text/javascript' src="{{ MEDIA_URL }}js/jquery-ui-1.8.10.custom.min.js"></script>
|
<script type='text/javascript' src="{{ MEDIA_URL }}js/jquery-ui-1.8.10.custom.min.js"></script>
|
||||||
|
@ -23,6 +24,7 @@
|
||||||
<script type='text/javascript' src="{{ MEDIA_URL }}js/jquery.gritter.min.js"></script>
|
<script type='text/javascript' src="{{ MEDIA_URL }}js/jquery.gritter.min.js"></script>
|
||||||
<script type='text/javascript' src="{{ MEDIA_URL }}js/k4ever.js"></script>
|
<script type='text/javascript' src="{{ MEDIA_URL }}js/k4ever.js"></script>
|
||||||
{% block extrahead %}{% endblock %}
|
{% block extrahead %}{% endblock %}
|
||||||
|
{% endblock head %}
|
||||||
<title>Freitagsrundenkasse{% block "title" %}{% endblock %}</title>
|
<title>Freitagsrundenkasse{% block "title" %}{% endblock %}</title>
|
||||||
</head>
|
</head>
|
||||||
<body{%block bodyargs %}{%endblock%}>
|
<body{%block bodyargs %}{%endblock%}>
|
||||||
|
|
|
@ -29,8 +29,3 @@
|
||||||
<input type="hidden" name="next" value="{{ next }}" />
|
<input type="hidden" name="next" value="{{ next }}" />
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
|
||||||
<div class="notice">
|
|
||||||
<h2>Das Kassensystem ist 'Aktiv' - nutzt euren Frunden-Account</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,13 @@ jQuery(document).ready(function($) {
|
||||||
}, options), callback);
|
}, options), callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var updateBalance = function(newBalance) {
|
||||||
|
var balanceString = $("span.balance").text().split(": ");
|
||||||
|
balanceString[1] = parseFloat(newBalance).toFixed(2).replace(".", ",") + " €";
|
||||||
|
|
||||||
|
$("span.balance").text(balanceString.join(": "));
|
||||||
|
}
|
||||||
|
|
||||||
$.extend($.gritter.options, {
|
$.extend($.gritter.options, {
|
||||||
position: "bottom-right",
|
position: "bottom-right",
|
||||||
fade_in_speed: 250,
|
fade_in_speed: 250,
|
||||||
|
@ -61,6 +68,7 @@ jQuery(document).ready(function($) {
|
||||||
});
|
});
|
||||||
|
|
||||||
dataContainer.children().removeClass("locked");
|
dataContainer.children().removeClass("locked");
|
||||||
|
updateBalance(data.balance);
|
||||||
}, options);
|
}, options);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -121,7 +129,7 @@ jQuery(document).ready(function($) {
|
||||||
|
|
||||||
if(status === "success") {
|
if(status === "success") {
|
||||||
selected_item.addClass("success");
|
selected_item.addClass("success");
|
||||||
//TODO neuer kontostand wär schnaffte
|
updateBalance(data.balance);
|
||||||
} else {
|
} else {
|
||||||
selected_item.addClass("failure");
|
selected_item.addClass("failure");
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ import shutil
|
||||||
sys.path.insert(0, '../client-barcode') # TODO
|
sys.path.insert(0, '../client-barcode') # TODO
|
||||||
from freitagslib.network import _request
|
from freitagslib.network import _request
|
||||||
from freitagslib.item import Item
|
from freitagslib.item import Item
|
||||||
|
from freitagslib.settings import settings, Settings
|
||||||
|
|
||||||
|
|
||||||
_NAME_BLACKLIST = ('foo', )
|
_NAME_BLACKLIST = ('foo', )
|
||||||
|
@ -38,6 +39,9 @@ if __name__ == '__main__':
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
output_filename = sys.argv[1]
|
output_filename = sys.argv[1]
|
||||||
|
|
||||||
|
if not settings.load('../client-barcode/freitagskasse.conf'):
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
content = _request('buyable/item/', None, method='GET')
|
content = _request('buyable/item/', None, method='GET')
|
||||||
doc = json.loads(content)
|
doc = json.loads(content)
|
||||||
items = [Item(e) for e in doc]
|
items = [Item(e) for e in doc]
|
||||||
|
|
Loading…
Reference in New Issue