format code with `black`
This commit is contained in:
parent
88d855a951
commit
c219fa61ba
4
conf.py
4
conf.py
|
@ -1,8 +1,8 @@
|
|||
db_url = 'postgresql://robertem@127.0.0.1/pvvvv'
|
||||
db_url = "postgresql://robertem@127.0.0.1/pvvvv"
|
||||
quit_allowed = True
|
||||
stop_allowed = False
|
||||
show_tracebacks = True
|
||||
input_encoding = 'utf8'
|
||||
input_encoding = "utf8"
|
||||
|
||||
low_credit_warning_limit = -100
|
||||
user_recent_transaction_limit = 100
|
||||
|
|
|
@ -3,5 +3,5 @@ from sqlalchemy.orm import sessionmaker
|
|||
|
||||
from dibbler.conf import config
|
||||
|
||||
engine = create_engine(config.get('database', 'url'))
|
||||
engine = create_engine(config.get("database", "url"))
|
||||
Session = sessionmaker(bind=engine)
|
|
@ -6,20 +6,20 @@ from brother_ql.devicedependent import label_type_specs
|
|||
|
||||
|
||||
def px2mm(px, dpi=300):
|
||||
return (25.4 * px)/dpi
|
||||
return (25.4 * px) / dpi
|
||||
|
||||
|
||||
class BrotherLabelWriter(ImageWriter):
|
||||
def __init__(self, typ='62', max_height=350, rot=False, text=None):
|
||||
def __init__(self, typ="62", max_height=350, rot=False, text=None):
|
||||
super(BrotherLabelWriter, self).__init__()
|
||||
assert typ in label_type_specs
|
||||
self.rot = rot
|
||||
if self.rot:
|
||||
self._h, self._w = label_type_specs[typ]['dots_printable']
|
||||
self._h, self._w = label_type_specs[typ]["dots_printable"]
|
||||
if self._w == 0 or self._w > max_height:
|
||||
self._w = min(max_height, self._h / 2)
|
||||
else:
|
||||
self._w, self._h = label_type_specs[typ]['dots_printable']
|
||||
self._w, self._h = label_type_specs[typ]["dots_printable"]
|
||||
if self._h == 0 or self._h > max_height:
|
||||
self._h = min(max_height, self._w / 2)
|
||||
self._xo = 0.0
|
||||
|
@ -31,36 +31,40 @@ class BrotherLabelWriter(ImageWriter):
|
|||
super(BrotherLabelWriter, self)._init(code)
|
||||
|
||||
def calculate_size(self, modules_per_line, number_of_lines, dpi=300):
|
||||
x, y = super(BrotherLabelWriter, self).calculate_size(modules_per_line, number_of_lines, dpi)
|
||||
x, y = super(BrotherLabelWriter, self).calculate_size(
|
||||
modules_per_line, number_of_lines, dpi
|
||||
)
|
||||
|
||||
self._xo = (px2mm(self._w)-px2mm(x))/2
|
||||
self._yo = (px2mm(self._h)-px2mm(y))
|
||||
self._xo = (px2mm(self._w) - px2mm(x)) / 2
|
||||
self._yo = px2mm(self._h) - px2mm(y)
|
||||
assert self._xo >= 0
|
||||
assert self._yo >= 0
|
||||
|
||||
return int(self._w), int(self._h)
|
||||
|
||||
def _paint_module(self, xpos, ypos, width, color):
|
||||
super(BrotherLabelWriter, self)._paint_module(xpos+self._xo, ypos+self._yo, width, color)
|
||||
super(BrotherLabelWriter, self)._paint_module(
|
||||
xpos + self._xo, ypos + self._yo, width, color
|
||||
)
|
||||
|
||||
def _paint_text(self, xpos, ypos):
|
||||
super(BrotherLabelWriter, self)._paint_text(xpos+self._xo, ypos+self._yo)
|
||||
super(BrotherLabelWriter, self)._paint_text(xpos + self._xo, ypos + self._yo)
|
||||
|
||||
def _finish(self):
|
||||
if self._title:
|
||||
width = self._w+1
|
||||
width = self._w + 1
|
||||
height = 0
|
||||
max_h = self._h - mm2px(self._yo, self.dpi)
|
||||
fs = int(max_h / 1.2)
|
||||
font_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "Stranger back in the Night.ttf")
|
||||
font_path = os.path.join(
|
||||
os.path.dirname(os.path.realpath(__file__)),
|
||||
"Stranger back in the Night.ttf",
|
||||
)
|
||||
font = ImageFont.truetype(font_path, 10)
|
||||
while width > self._w or height > max_h:
|
||||
font = ImageFont.truetype(font_path, fs)
|
||||
width, height = font.getsize(self._title)
|
||||
fs -= 1
|
||||
pos = (
|
||||
(self._w-width)//2,
|
||||
0 - (height // 8)
|
||||
)
|
||||
pos = ((self._w - width) // 2, 0 - (height // 8))
|
||||
self._draw.text(pos, self._title, font=font, fill=self.foreground)
|
||||
return self._image
|
||||
|
|
|
@ -7,31 +7,72 @@ from sqlalchemy import or_, and_
|
|||
|
||||
from ..models import User, Product
|
||||
|
||||
|
||||
def search_user(string, session, ignorethisflag=None):
|
||||
string = string.lower()
|
||||
exact_match = session.query(User).filter(or_(User.name == string, User.card == string, User.rfid == string)).first()
|
||||
exact_match = (
|
||||
session.query(User)
|
||||
.filter(or_(User.name == string, User.card == string, User.rfid == string))
|
||||
.first()
|
||||
)
|
||||
if exact_match:
|
||||
return exact_match
|
||||
user_list = session.query(User).filter(or_(User.name.ilike(f'%{string}%'),
|
||||
User.card.ilike(f'%{string}%'),
|
||||
User.rfid.ilike(f'%{string}%'))).all()
|
||||
user_list = (
|
||||
session.query(User)
|
||||
.filter(
|
||||
or_(
|
||||
User.name.ilike(f"%{string}%"),
|
||||
User.card.ilike(f"%{string}%"),
|
||||
User.rfid.ilike(f"%{string}%"),
|
||||
)
|
||||
)
|
||||
.all()
|
||||
)
|
||||
return user_list
|
||||
|
||||
|
||||
def search_product(string, session, find_hidden_products=True):
|
||||
if find_hidden_products:
|
||||
exact_match = session.query(Product).filter(or_(Product.bar_code == string, Product.name == string)).first()
|
||||
exact_match = (
|
||||
session.query(Product)
|
||||
.filter(or_(Product.bar_code == string, Product.name == string))
|
||||
.first()
|
||||
)
|
||||
else:
|
||||
exact_match = session.query(Product).filter(or_(Product.bar_code == string,
|
||||
and_(Product.name == string, Product.hidden == False))).first()
|
||||
exact_match = (
|
||||
session.query(Product)
|
||||
.filter(
|
||||
or_(
|
||||
Product.bar_code == string,
|
||||
and_(Product.name == string, Product.hidden == False),
|
||||
)
|
||||
)
|
||||
.first()
|
||||
)
|
||||
if exact_match:
|
||||
return exact_match
|
||||
if find_hidden_products:
|
||||
product_list = session.query(Product).filter(or_(Product.bar_code.ilike(f'%{string}%'),
|
||||
Product.name.ilike(f'%{string}%'))).all()
|
||||
product_list = (
|
||||
session.query(Product)
|
||||
.filter(
|
||||
or_(
|
||||
Product.bar_code.ilike(f"%{string}%"),
|
||||
Product.name.ilike(f"%{string}%"),
|
||||
)
|
||||
)
|
||||
.all()
|
||||
)
|
||||
else:
|
||||
product_list = session.query(Product).filter(or_(Product.bar_code.ilike(f'%{string}%'),
|
||||
and_(Product.name.ilike(f'%{string}%'),
|
||||
Product.hidden == False))).all()
|
||||
product_list = (
|
||||
session.query(Product)
|
||||
.filter(
|
||||
or_(
|
||||
Product.bar_code.ilike(f"%{string}%"),
|
||||
and_(Product.name.ilike(f"%{string}%"), Product.hidden == False),
|
||||
)
|
||||
)
|
||||
.all()
|
||||
)
|
||||
return product_list
|
||||
|
||||
|
||||
|
@ -45,61 +86,21 @@ def system_user_exists(username):
|
|||
else:
|
||||
return True
|
||||
|
||||
|
||||
def guess_data_type(string):
|
||||
if string.startswith('ntnu') and string[4:].isdigit():
|
||||
return 'card'
|
||||
if string.startswith("ntnu") and string[4:].isdigit():
|
||||
return "card"
|
||||
if string.isdigit() and len(string) == 10:
|
||||
return 'rfid'
|
||||
if string.isdigit() and len(string) in [8,13]:
|
||||
return 'bar_code'
|
||||
# if string.isdigit() and len(string) > 5:
|
||||
# return 'card'
|
||||
return "rfid"
|
||||
if string.isdigit() and len(string) in [8, 13]:
|
||||
return "bar_code"
|
||||
# if string.isdigit() and len(string) > 5:
|
||||
# return 'card'
|
||||
if string.isalpha() and string.islower() and system_user_exists(string):
|
||||
return 'username'
|
||||
return "username"
|
||||
return None
|
||||
|
||||
|
||||
# def retrieve_user(string, session):
|
||||
# # first = session.query(User).filter(or_(User.name==string, User.card==string)).first()
|
||||
# search = search_user(string,session)
|
||||
# if isinstance(search,User):
|
||||
# print "Found user "+search.name
|
||||
# return search
|
||||
# else:
|
||||
# if len(search) == 0:
|
||||
# print "No users found matching your search"
|
||||
# return None
|
||||
# if len(search) == 1:
|
||||
# print "Found one user: "+list[0].name
|
||||
# if confirm():
|
||||
# return list[0]
|
||||
# else:
|
||||
# return None
|
||||
# else:
|
||||
# print "Found "+str(len(search))+" users:"
|
||||
# return select_from_list(search)
|
||||
|
||||
|
||||
# def confirm(prompt='Confirm? (y/n) '):
|
||||
# while True:
|
||||
# input = raw_input(prompt)
|
||||
# if input in ["y","yes"]:
|
||||
# return True
|
||||
# elif input in ["n","no"]:
|
||||
# return False
|
||||
# else:
|
||||
# print "Nonsense!"
|
||||
|
||||
# def select_from_list(list):
|
||||
# while True:
|
||||
# for i in range(len(list)):
|
||||
# print i+1, " ) ", list[i].name
|
||||
# choice = raw_input("Select user :\n")
|
||||
# if choice in [str(x+1) for x in range(len(list))]:
|
||||
# return list[int(choice)-1]
|
||||
# else:
|
||||
# return None
|
||||
|
||||
def argmax(d, all=False, value=None):
|
||||
maxarg = None
|
||||
maxargs = []
|
||||
|
@ -117,14 +118,14 @@ def argmax(d, all=False, value=None):
|
|||
|
||||
|
||||
def less(string):
|
||||
'''
|
||||
"""
|
||||
Run less with string as input; wait until it finishes.
|
||||
'''
|
||||
"""
|
||||
# If we don't ignore SIGINT while running the `less` process,
|
||||
# it will become a zombie when someone presses C-c.
|
||||
int_handler = signal.signal(signal.SIGINT, signal.SIG_IGN)
|
||||
env = dict(os.environ)
|
||||
env['LESSSECURE'] = '1'
|
||||
proc = subprocess.Popen('less', env=env, encoding='utf-8', stdin=subprocess.PIPE)
|
||||
env["LESSSECURE"] = "1"
|
||||
proc = subprocess.Popen("less", env=env, encoding="utf-8", stdin=subprocess.PIPE)
|
||||
proc.communicate(string)
|
||||
signal.signal(signal.SIGINT, int_handler)
|
||||
|
|
|
@ -10,35 +10,41 @@ from PIL import Image, ImageDraw, ImageFont
|
|||
from .barcode_helpers import BrotherLabelWriter
|
||||
|
||||
|
||||
def print_name_label(text, margin=10, rotate=False, label_type="62", printer_type="QL-700",):
|
||||
def print_name_label(
|
||||
text,
|
||||
margin=10,
|
||||
rotate=False,
|
||||
label_type="62",
|
||||
printer_type="QL-700",
|
||||
):
|
||||
if not rotate:
|
||||
width, height = label_type_specs[label_type]['dots_printable']
|
||||
width, height = label_type_specs[label_type]["dots_printable"]
|
||||
else:
|
||||
height, width = label_type_specs[label_type]['dots_printable']
|
||||
height, width = label_type_specs[label_type]["dots_printable"]
|
||||
|
||||
font_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "ChopinScript.ttf")
|
||||
fs = 2000
|
||||
tw, th = width, height
|
||||
if width == 0:
|
||||
while th + 2*margin > height:
|
||||
while th + 2 * margin > height:
|
||||
font = ImageFont.truetype(font_path, fs)
|
||||
tw, th = font.getsize(text)
|
||||
fs -= 1
|
||||
width = tw+2*margin
|
||||
width = tw + 2 * margin
|
||||
elif height == 0:
|
||||
while tw + 2*margin > width:
|
||||
while tw + 2 * margin > width:
|
||||
font = ImageFont.truetype(font_path, fs)
|
||||
tw, th = font.getsize(text)
|
||||
fs -= 1
|
||||
height = th+2*margin
|
||||
height = th + 2 * margin
|
||||
else:
|
||||
while tw + 2*margin > width or th + 2*margin > height:
|
||||
while tw + 2 * margin > width or th + 2 * margin > height:
|
||||
font = ImageFont.truetype(font_path, fs)
|
||||
tw, th = font.getsize(text)
|
||||
fs -= 1
|
||||
|
||||
xp = (width//2)-(tw//2)
|
||||
yp = (height//2)-(th//2)
|
||||
xp = (width // 2) - (tw // 2)
|
||||
yp = (height // 2) - (th // 2)
|
||||
|
||||
im = Image.new("RGB", (width, height), (255, 255, 255))
|
||||
dr = ImageDraw.Draw(im)
|
||||
|
@ -55,8 +61,14 @@ def print_name_label(text, margin=10, rotate=False, label_type="62", printer_typ
|
|||
print_image(fn, printer_type, label_type)
|
||||
|
||||
|
||||
def print_bar_code(barcode_value, barcode_text, barcode_type="ean13", rotate=False, printer_type="QL-700",
|
||||
label_type="62"):
|
||||
def print_bar_code(
|
||||
barcode_value,
|
||||
barcode_text,
|
||||
barcode_type="ean13",
|
||||
rotate=False,
|
||||
printer_type="QL-700",
|
||||
label_type="62",
|
||||
):
|
||||
bar_coder = barcode.get_barcode_class(barcode_type)
|
||||
wr = BrotherLabelWriter(typ=label_type, rot=rotate, text=barcode_text, max_height=1000)
|
||||
|
||||
|
@ -72,12 +84,12 @@ def print_image(fn, printer_type="QL-700", label_type="62"):
|
|||
create_label(qlr, fn, label_type, threshold=70, cut=True)
|
||||
|
||||
be = backend_factory("pyusb")
|
||||
list_available_devices = be['list_available_devices']
|
||||
BrotherQLBackend = be['backend_class']
|
||||
list_available_devices = be["list_available_devices"]
|
||||
BrotherQLBackend = be["backend_class"]
|
||||
|
||||
ad = list_available_devices()
|
||||
assert ad
|
||||
string_descr = ad[0]['string_descr']
|
||||
string_descr = ad[0]["string_descr"]
|
||||
|
||||
printer = BrotherQLBackend(string_descr)
|
||||
|
||||
|
|
|
@ -8,411 +8,507 @@ from .helpers import *
|
|||
from ..models import Transaction
|
||||
from ..db import Session
|
||||
|
||||
|
||||
def getUser():
|
||||
while 1:
|
||||
string = input('user? ')
|
||||
session = Session()
|
||||
user = search_user(string, session)
|
||||
session.close()
|
||||
if not isinstance(user, list):
|
||||
return user.name
|
||||
i=0
|
||||
if len(user)==0:
|
||||
print('no matching string')
|
||||
if len(user)==1:
|
||||
print('antar: ', user[0].name, '\n')
|
||||
return user[0].name
|
||||
if len(user)>10:
|
||||
continue
|
||||
for u in user:
|
||||
print(i, u.name)
|
||||
i += 1
|
||||
try:
|
||||
n = int(input ('enter number:'))
|
||||
except:
|
||||
print('invalid input, restarting')
|
||||
continue
|
||||
if (n>-1) and (n<i):
|
||||
return user[n].name
|
||||
while 1:
|
||||
string = input("user? ")
|
||||
session = Session()
|
||||
user = search_user(string, session)
|
||||
session.close()
|
||||
if not isinstance(user, list):
|
||||
return user.name
|
||||
i = 0
|
||||
if len(user) == 0:
|
||||
print("no matching string")
|
||||
if len(user) == 1:
|
||||
print("antar: ", user[0].name, "\n")
|
||||
return user[0].name
|
||||
if len(user) > 10:
|
||||
continue
|
||||
for u in user:
|
||||
print(i, u.name)
|
||||
i += 1
|
||||
try:
|
||||
n = int(input("enter number:"))
|
||||
except:
|
||||
print("invalid input, restarting")
|
||||
continue
|
||||
if (n > -1) and (n < i):
|
||||
return user[n].name
|
||||
|
||||
|
||||
def getProduct():
|
||||
while 1:
|
||||
string = input('product? ')
|
||||
session = Session()
|
||||
product = search_product(string, session)
|
||||
session.close()
|
||||
if not isinstance(product, list):
|
||||
return product.name
|
||||
i=0
|
||||
if len(product)==0:
|
||||
print('no matching string')
|
||||
if len(product)==1:
|
||||
print('antar: ', product[0].name, '\n')
|
||||
return product[0].name
|
||||
if len(product)>10:
|
||||
continue
|
||||
for u in product:
|
||||
print(i, u.name)
|
||||
i += 1
|
||||
try:
|
||||
n = int(input ('enter number:'))
|
||||
except:
|
||||
print('invalid input, restarting')
|
||||
continue
|
||||
if (n>-1) and (n<i):
|
||||
return product[n].name
|
||||
while 1:
|
||||
string = input("product? ")
|
||||
session = Session()
|
||||
product = search_product(string, session)
|
||||
session.close()
|
||||
if not isinstance(product, list):
|
||||
return product.name
|
||||
i = 0
|
||||
if len(product) == 0:
|
||||
print("no matching string")
|
||||
if len(product) == 1:
|
||||
print("antar: ", product[0].name, "\n")
|
||||
return product[0].name
|
||||
if len(product) > 10:
|
||||
continue
|
||||
for u in product:
|
||||
print(i, u.name)
|
||||
i += 1
|
||||
try:
|
||||
n = int(input("enter number:"))
|
||||
except:
|
||||
print("invalid input, restarting")
|
||||
continue
|
||||
if (n > -1) and (n < i):
|
||||
return product[n].name
|
||||
|
||||
|
||||
class Database:
|
||||
#for varer
|
||||
varePersonAntall = defaultdict(dict) #varePersonAntall[Oreo][trygvrad] == 3
|
||||
vareDatoAntall = defaultdict(list) #dict->array
|
||||
vareUkedagAntall = defaultdict(list)
|
||||
#for personer
|
||||
personVareAntall = defaultdict(dict) #personVareAntall[trygvrad][Oreo] == 3
|
||||
personVareVerdi = defaultdict(dict) #personVareVerdi[trygvrad][Oreo] == 30 #[kr]
|
||||
personDatoVerdi = defaultdict(list) #dict->array
|
||||
personUkedagVerdi = defaultdict(list)
|
||||
#for global
|
||||
personPosTransactions = {} # personPosTransactions[trygvrad] == 100 #trygvrad har lagt 100kr i boksen
|
||||
personNegTransactions = {} # personNegTransactions[trygvrad» == 70 #trygvrad har tatt 70kr fra boksen
|
||||
globalVareAntall = {}#globalVareAntall[Oreo] == 3
|
||||
globalVareVerdi = {}#globalVareVerdi[Oreo] == 30 #[kr]
|
||||
globalPersonAntall = {}#globalPersonAntall[trygvrad] == 3
|
||||
globalPersonForbruk = {}#globalPersonVerdi == 30 #[kr]
|
||||
globalUkedagForbruk = []
|
||||
globalDatoVarer = []
|
||||
globalDatoForbruk = []
|
||||
pengebeholdning = []
|
||||
# for varer
|
||||
varePersonAntall = defaultdict(dict) # varePersonAntall[Oreo][trygvrad] == 3
|
||||
vareDatoAntall = defaultdict(list) # dict->array
|
||||
vareUkedagAntall = defaultdict(list)
|
||||
# for personer
|
||||
personVareAntall = defaultdict(dict) # personVareAntall[trygvrad][Oreo] == 3
|
||||
personVareVerdi = defaultdict(dict) # personVareVerdi[trygvrad][Oreo] == 30 #[kr]
|
||||
personDatoVerdi = defaultdict(list) # dict->array
|
||||
personUkedagVerdi = defaultdict(list)
|
||||
# for global
|
||||
personPosTransactions = (
|
||||
{}
|
||||
) # personPosTransactions[trygvrad] == 100 #trygvrad har lagt 100kr i boksen
|
||||
personNegTransactions = (
|
||||
{}
|
||||
) # personNegTransactions[trygvrad» == 70 #trygvrad har tatt 70kr fra boksen
|
||||
globalVareAntall = {} # globalVareAntall[Oreo] == 3
|
||||
globalVareVerdi = {} # globalVareVerdi[Oreo] == 30 #[kr]
|
||||
globalPersonAntall = {} # globalPersonAntall[trygvrad] == 3
|
||||
globalPersonForbruk = {} # globalPersonVerdi == 30 #[kr]
|
||||
globalUkedagForbruk = []
|
||||
globalDatoVarer = []
|
||||
globalDatoForbruk = []
|
||||
pengebeholdning = []
|
||||
|
||||
|
||||
class InputLine:
|
||||
def __init__(self, u, p, t):
|
||||
self.inputUser = u
|
||||
self.inputProduct = p
|
||||
self.inputType = t
|
||||
def __init__(self, u, p, t):
|
||||
self.inputUser = u
|
||||
self.inputProduct = p
|
||||
self.inputType = t
|
||||
|
||||
|
||||
def getDateDb(date, inp):
|
||||
try:
|
||||
year = inp.partition('-')
|
||||
month = year[2].partition('-')
|
||||
return datetime.datetime(int(year[0]), int(month[0]), int(month[2]))
|
||||
except:
|
||||
print('invalid date, setting date to date found in db')
|
||||
print(date)
|
||||
return date
|
||||
try:
|
||||
year = inp.partition("-")
|
||||
month = year[2].partition("-")
|
||||
return datetime.datetime(int(year[0]), int(month[0]), int(month[2]))
|
||||
except:
|
||||
print("invalid date, setting date to date found in db")
|
||||
print(date)
|
||||
return date
|
||||
|
||||
|
||||
def dateToDateNumDb(date, startDate):
|
||||
deltaDays = date-startDate
|
||||
return int(deltaDays.days), date.weekday()
|
||||
deltaDays = date - startDate
|
||||
return int(deltaDays.days), date.weekday()
|
||||
|
||||
|
||||
def getInputType():
|
||||
inp = 0
|
||||
while not (inp == '1' or inp == '2' or inp == '3' or inp == '4'):
|
||||
print('type 1 for user-statistics')
|
||||
print('type 2 for product-statistics')
|
||||
print('type 3 for global-statistics')
|
||||
print('type 4 to enter loop-mode')
|
||||
inp = input('')
|
||||
return int(inp)
|
||||
inp = 0
|
||||
while not (inp == "1" or inp == "2" or inp == "3" or inp == "4"):
|
||||
print("type 1 for user-statistics")
|
||||
print("type 2 for product-statistics")
|
||||
print("type 3 for global-statistics")
|
||||
print("type 4 to enter loop-mode")
|
||||
inp = input("")
|
||||
return int(inp)
|
||||
|
||||
|
||||
def getProducts(products):
|
||||
product = []
|
||||
products = products.partition('¤')
|
||||
product.append(products[0])
|
||||
while (products[1]=='¤'):
|
||||
products = products[2].partition('¤')
|
||||
product.append(products[0])
|
||||
return product
|
||||
product = []
|
||||
products = products.partition("¤")
|
||||
product.append(products[0])
|
||||
while products[1] == "¤":
|
||||
products = products[2].partition("¤")
|
||||
product.append(products[0])
|
||||
return product
|
||||
|
||||
|
||||
def getDateFile(date, inp):
|
||||
try:
|
||||
year = inp.partition('-')
|
||||
month = year[2].partition('-')
|
||||
return datetime.date(int(year[0]), int(month[0]), int(month[2]))
|
||||
except:
|
||||
print('invalid date, setting date to date found on file file')
|
||||
print(date)
|
||||
return datetime.date(int(date.partition('-')[0]), int(date.partition('-')[2].partition('-')[0]), int(date.partition('-')[2].partition('-')[2]))
|
||||
try:
|
||||
year = inp.partition("-")
|
||||
month = year[2].partition("-")
|
||||
return datetime.date(int(year[0]), int(month[0]), int(month[2]))
|
||||
except:
|
||||
print("invalid date, setting date to date found on file file")
|
||||
print(date)
|
||||
return datetime.date(
|
||||
int(date.partition("-")[0]),
|
||||
int(date.partition("-")[2].partition("-")[0]),
|
||||
int(date.partition("-")[2].partition("-")[2]),
|
||||
)
|
||||
|
||||
|
||||
def dateToDateNumFile(date, startDate):
|
||||
year = date.partition('-')
|
||||
month = year[2].partition('-')
|
||||
day = datetime.date(int(year[0]), int(month[0]), int(month[2]))
|
||||
deltaDays = day-startDate
|
||||
return int(deltaDays.days), day.weekday()
|
||||
year = date.partition("-")
|
||||
month = year[2].partition("-")
|
||||
day = datetime.date(int(year[0]), int(month[0]), int(month[2]))
|
||||
deltaDays = day - startDate
|
||||
return int(deltaDays.days), day.weekday()
|
||||
|
||||
|
||||
def clearDatabase(database):
|
||||
database.varePersonAntall.clear()
|
||||
database.vareDatoAntall.clear()
|
||||
database.vareUkedagAntall.clear()
|
||||
database.personVareAntall.clear()
|
||||
database.personVareVerdi.clear()
|
||||
database.personDatoVerdi.clear()
|
||||
database.personUkedagVerdi.clear()
|
||||
database.personPosTransactions.clear()
|
||||
database.personNegTransactions.clear()
|
||||
database.globalVareAntall.clear()
|
||||
database.globalVareVerdi.clear()
|
||||
database.globalPersonAntall.clear()
|
||||
database.globalPersonForbruk.clear()
|
||||
return(database)
|
||||
database.varePersonAntall.clear()
|
||||
database.vareDatoAntall.clear()
|
||||
database.vareUkedagAntall.clear()
|
||||
database.personVareAntall.clear()
|
||||
database.personVareVerdi.clear()
|
||||
database.personDatoVerdi.clear()
|
||||
database.personUkedagVerdi.clear()
|
||||
database.personPosTransactions.clear()
|
||||
database.personNegTransactions.clear()
|
||||
database.globalVareAntall.clear()
|
||||
database.globalVareVerdi.clear()
|
||||
database.globalPersonAntall.clear()
|
||||
database.globalPersonForbruk.clear()
|
||||
return database
|
||||
|
||||
|
||||
def addLineToDatabase(database, inputLine):
|
||||
if abs(inputLine.price)>90000:
|
||||
return database
|
||||
#fyller inn for varer
|
||||
if (not inputLine.product=='') and ((inputLine.inputProduct=='') or (inputLine.inputProduct==inputLine.product)):
|
||||
database.varePersonAntall[inputLine.product][inputLine.user] = database.varePersonAntall[inputLine.product].setdefault(inputLine.user,0) + 1
|
||||
if inputLine.product not in database.vareDatoAntall:
|
||||
database.vareDatoAntall[inputLine.product] = [0]*(inputLine.numberOfDays+1)
|
||||
database.vareDatoAntall[inputLine.product][inputLine.dateNum] += 1
|
||||
if inputLine.product not in database.vareUkedagAntall:
|
||||
database.vareUkedagAntall[inputLine.product] = [0]*7
|
||||
database.vareUkedagAntall[inputLine.product][inputLine.weekday] += 1
|
||||
#fyller inn for personer
|
||||
if (inputLine.inputUser=='') or (inputLine.inputUser==inputLine.user):
|
||||
if not inputLine.product == '':
|
||||
database.personVareAntall[inputLine.user][inputLine.product] = database.personVareAntall[inputLine.user].setdefault(inputLine.product,0) + 1
|
||||
database.personVareVerdi[inputLine.user][inputLine.product] = database.personVareVerdi[inputLine.user].setdefault(inputLine.product,0) + inputLine.price
|
||||
if inputLine.user not in database.personDatoVerdi:
|
||||
database.personDatoVerdi[inputLine.user] = [0]*(inputLine.numberOfDays+1)
|
||||
database.personDatoVerdi[inputLine.user][inputLine.dateNum] += inputLine.price
|
||||
if inputLine.user not in database.personUkedagVerdi:
|
||||
database.personUkedagVerdi[inputLine.user] = [0]*7
|
||||
database.personUkedagVerdi[inputLine.user][inputLine.weekday] += inputLine.price
|
||||
#fyller inn delt statistikk (genereres uansett)
|
||||
if (inputLine.product==''):
|
||||
if (inputLine.price>0):
|
||||
database.personPosTransactions[inputLine.user] = database.personPosTransactions.setdefault(inputLine.user,0) + inputLine.price
|
||||
else:
|
||||
database.personNegTransactions[inputLine.user] = database.personNegTransactions.setdefault(inputLine.user,0) + inputLine.price
|
||||
elif not (inputLine.inputType==1):
|
||||
database.globalVareAntall[inputLine.product] = database.globalVareAntall.setdefault(inputLine.product,0) + 1
|
||||
database.globalVareVerdi[inputLine.product] = database.globalVareVerdi.setdefault(inputLine.product,0) + inputLine.price
|
||||
if abs(inputLine.price) > 90000:
|
||||
return database
|
||||
# fyller inn for varer
|
||||
if (not inputLine.product == "") and (
|
||||
(inputLine.inputProduct == "") or (inputLine.inputProduct == inputLine.product)
|
||||
):
|
||||
database.varePersonAntall[inputLine.product][inputLine.user] = (
|
||||
database.varePersonAntall[inputLine.product].setdefault(inputLine.user, 0) + 1
|
||||
)
|
||||
if inputLine.product not in database.vareDatoAntall:
|
||||
database.vareDatoAntall[inputLine.product] = [0] * (inputLine.numberOfDays + 1)
|
||||
database.vareDatoAntall[inputLine.product][inputLine.dateNum] += 1
|
||||
if inputLine.product not in database.vareUkedagAntall:
|
||||
database.vareUkedagAntall[inputLine.product] = [0] * 7
|
||||
database.vareUkedagAntall[inputLine.product][inputLine.weekday] += 1
|
||||
# fyller inn for personer
|
||||
if (inputLine.inputUser == "") or (inputLine.inputUser == inputLine.user):
|
||||
if not inputLine.product == "":
|
||||
database.personVareAntall[inputLine.user][inputLine.product] = (
|
||||
database.personVareAntall[inputLine.user].setdefault(inputLine.product, 0) + 1
|
||||
)
|
||||
database.personVareVerdi[inputLine.user][inputLine.product] = (
|
||||
database.personVareVerdi[inputLine.user].setdefault(inputLine.product, 0)
|
||||
+ inputLine.price
|
||||
)
|
||||
if inputLine.user not in database.personDatoVerdi:
|
||||
database.personDatoVerdi[inputLine.user] = [0] * (inputLine.numberOfDays + 1)
|
||||
database.personDatoVerdi[inputLine.user][inputLine.dateNum] += inputLine.price
|
||||
if inputLine.user not in database.personUkedagVerdi:
|
||||
database.personUkedagVerdi[inputLine.user] = [0] * 7
|
||||
database.personUkedagVerdi[inputLine.user][inputLine.weekday] += inputLine.price
|
||||
# fyller inn delt statistikk (genereres uansett)
|
||||
if inputLine.product == "":
|
||||
if inputLine.price > 0:
|
||||
database.personPosTransactions[inputLine.user] = (
|
||||
database.personPosTransactions.setdefault(inputLine.user, 0) + inputLine.price
|
||||
)
|
||||
else:
|
||||
database.personNegTransactions[inputLine.user] = (
|
||||
database.personNegTransactions.setdefault(inputLine.user, 0) + inputLine.price
|
||||
)
|
||||
elif not (inputLine.inputType == 1):
|
||||
database.globalVareAntall[inputLine.product] = (
|
||||
database.globalVareAntall.setdefault(inputLine.product, 0) + 1
|
||||
)
|
||||
database.globalVareVerdi[inputLine.product] = (
|
||||
database.globalVareVerdi.setdefault(inputLine.product, 0) + inputLine.price
|
||||
)
|
||||
|
||||
# fyller inn for global statistikk
|
||||
if (inputLine.inputType == 3) or (inputLine.inputType == 4):
|
||||
database.pengebeholdning[inputLine.dateNum] += inputLine.price
|
||||
if not (inputLine.product == ""):
|
||||
database.globalPersonAntall[inputLine.user] = (
|
||||
database.globalPersonAntall.setdefault(inputLine.user, 0) + 1
|
||||
)
|
||||
database.globalPersonForbruk[inputLine.user] = (
|
||||
database.globalPersonForbruk.setdefault(inputLine.user, 0) + inputLine.price
|
||||
)
|
||||
database.globalDatoVarer[inputLine.dateNum] += 1
|
||||
database.globalDatoForbruk[inputLine.dateNum] += inputLine.price
|
||||
database.globalUkedagForbruk[inputLine.weekday] += inputLine.price
|
||||
return database
|
||||
|
||||
#fyller inn for global statistikk
|
||||
if (inputLine.inputType==3) or (inputLine.inputType==4):
|
||||
database.pengebeholdning[inputLine.dateNum] += inputLine.price
|
||||
if not (inputLine.product==''):
|
||||
database.globalPersonAntall[inputLine.user] = database.globalPersonAntall.setdefault(inputLine.user,0) + 1
|
||||
database.globalPersonForbruk[inputLine.user] = database.globalPersonForbruk.setdefault(inputLine.user,0) + inputLine.price
|
||||
database.globalDatoVarer[inputLine.dateNum] += 1
|
||||
database.globalDatoForbruk[inputLine.dateNum] += inputLine.price
|
||||
database.globalUkedagForbruk[inputLine.weekday] += inputLine.price
|
||||
return database
|
||||
|
||||
def buildDatabaseFromDb(inputType, inputProduct, inputUser):
|
||||
sdate = input('enter start date (yyyy-mm-dd)? ')
|
||||
edate = input('enter end date (yyyy-mm-dd)? ')
|
||||
print('building database...')
|
||||
session = Session()
|
||||
transaction_list = session.query(Transaction).all()
|
||||
inputLine = InputLine(inputUser, inputProduct, inputType)
|
||||
startDate = getDateDb(transaction_list[0].time, sdate)
|
||||
endDate = getDateDb(transaction_list[-1].time, edate)
|
||||
inputLine.numberOfDays = (endDate-startDate).days
|
||||
database = Database()
|
||||
database = clearDatabase(database)
|
||||
sdate = input("enter start date (yyyy-mm-dd)? ")
|
||||
edate = input("enter end date (yyyy-mm-dd)? ")
|
||||
print("building database...")
|
||||
session = Session()
|
||||
transaction_list = session.query(Transaction).all()
|
||||
inputLine = InputLine(inputUser, inputProduct, inputType)
|
||||
startDate = getDateDb(transaction_list[0].time, sdate)
|
||||
endDate = getDateDb(transaction_list[-1].time, edate)
|
||||
inputLine.numberOfDays = (endDate - startDate).days
|
||||
database = Database()
|
||||
database = clearDatabase(database)
|
||||
|
||||
if (inputType==3) or (inputType==4):
|
||||
database.globalDatoVarer = [0]*(inputLine.numberOfDays+1)
|
||||
database.globalDatoForbruk = [0]*(inputLine.numberOfDays+1)
|
||||
database.globalUkedagForbruk = [0]*7
|
||||
database.pengebeholdning = [0]*(inputLine.numberOfDays+1)
|
||||
print('wait for it.... ')
|
||||
for transaction in transaction_list:
|
||||
if transaction.purchase:
|
||||
products = [ent.product.name for ent in transaction.purchase.entries]
|
||||
else:
|
||||
products = []
|
||||
products.append('')
|
||||
inputLine.dateNum, inputLine.weekday = dateToDateNumDb(transaction.time, startDate)
|
||||
if inputLine.dateNum<0 or inputLine.dateNum>(inputLine.numberOfDays):
|
||||
continue
|
||||
inputLine.user=transaction.user.name
|
||||
inputLine.price=transaction.amount
|
||||
for inputLine.product in products:
|
||||
database=addLineToDatabase(database, inputLine )
|
||||
inputLine.price = 0;
|
||||
if (inputType == 3) or (inputType == 4):
|
||||
database.globalDatoVarer = [0] * (inputLine.numberOfDays + 1)
|
||||
database.globalDatoForbruk = [0] * (inputLine.numberOfDays + 1)
|
||||
database.globalUkedagForbruk = [0] * 7
|
||||
database.pengebeholdning = [0] * (inputLine.numberOfDays + 1)
|
||||
print("wait for it.... ")
|
||||
for transaction in transaction_list:
|
||||
if transaction.purchase:
|
||||
products = [ent.product.name for ent in transaction.purchase.entries]
|
||||
else:
|
||||
products = []
|
||||
products.append("")
|
||||
inputLine.dateNum, inputLine.weekday = dateToDateNumDb(transaction.time, startDate)
|
||||
if inputLine.dateNum < 0 or inputLine.dateNum > (inputLine.numberOfDays):
|
||||
continue
|
||||
inputLine.user = transaction.user.name
|
||||
inputLine.price = transaction.amount
|
||||
for inputLine.product in products:
|
||||
database = addLineToDatabase(database, inputLine)
|
||||
inputLine.price = 0
|
||||
|
||||
print("saving as default.dibblerlog...", end=" ")
|
||||
f = open("default.dibblerlog", "w")
|
||||
line_format = "%s|%s|%s|%s|%s|%s\n"
|
||||
transaction_list = session.query(Transaction).all()
|
||||
for transaction in transaction_list:
|
||||
if transaction.purchase:
|
||||
products = "¤".join([ent.product.name for ent in transaction.purchase.entries])
|
||||
description = ""
|
||||
else:
|
||||
products = ""
|
||||
description = transaction.description
|
||||
line = line_format % (
|
||||
"purchase",
|
||||
transaction.time,
|
||||
products,
|
||||
transaction.user.name,
|
||||
transaction.amount,
|
||||
transaction.description,
|
||||
)
|
||||
f.write(line.encode("utf8"))
|
||||
session.close()
|
||||
f.close
|
||||
# bygg database.pengebeholdning
|
||||
if (inputType == 3) or (inputType == 4):
|
||||
for i in range(inputLine.numberOfDays + 1):
|
||||
if i > 0:
|
||||
database.pengebeholdning[i] += database.pengebeholdning[i - 1]
|
||||
# bygg dateLine
|
||||
day = datetime.timedelta(days=1)
|
||||
dateLine = []
|
||||
dateLine.append(startDate)
|
||||
for n in range(inputLine.numberOfDays):
|
||||
dateLine.append(startDate + n * day)
|
||||
print("done")
|
||||
return database, dateLine
|
||||
|
||||
print('saving as default.dibblerlog...', end=' ')
|
||||
f=open('default.dibblerlog','w')
|
||||
line_format = '%s|%s|%s|%s|%s|%s\n'
|
||||
transaction_list = session.query(Transaction).all()
|
||||
for transaction in transaction_list:
|
||||
if transaction.purchase:
|
||||
products = '¤'.join([ent.product.name for ent in transaction.purchase.entries])
|
||||
description = ''
|
||||
else:
|
||||
products = ''
|
||||
description = transaction.description
|
||||
line = line_format % ('purchase', transaction.time, products, transaction.user.name, transaction.amount, transaction.description)
|
||||
f.write(line.encode('utf8'))
|
||||
session.close()
|
||||
f.close
|
||||
#bygg database.pengebeholdning
|
||||
if (inputType==3) or (inputType==4):
|
||||
for i in range(inputLine.numberOfDays+1):
|
||||
if i > 0:
|
||||
database.pengebeholdning[i] +=database.pengebeholdning[i-1]
|
||||
#bygg dateLine
|
||||
day=datetime.timedelta(days=1)
|
||||
dateLine=[]
|
||||
dateLine.append(startDate)
|
||||
for n in range(inputLine.numberOfDays):
|
||||
dateLine.append(startDate+n*day)
|
||||
print('done')
|
||||
return database, dateLine
|
||||
|
||||
def buildDatabaseFromFile(inputFile, inputType, inputProduct, inputUser):
|
||||
sdate = input('enter start date (yyyy-mm-dd)? ')
|
||||
edate = input('enter end date (yyyy-mm-dd)? ')
|
||||
sdate = input("enter start date (yyyy-mm-dd)? ")
|
||||
edate = input("enter end date (yyyy-mm-dd)? ")
|
||||
|
||||
f=open(inputFile)
|
||||
try:
|
||||
fileLines=f.readlines()
|
||||
finally:
|
||||
f.close()
|
||||
inputLine = InputLine(inputUser, inputProduct, inputType)
|
||||
startDate = getDateFile(fileLines[0].partition('|')[2].partition(' ')[0], sdate)
|
||||
endDate = getDateFile(fileLines[-1].partition('|')[2].partition(' ')[0], edate)
|
||||
inputLine.numberOfDays = (endDate-startDate).days
|
||||
database = Database()
|
||||
database = clearDatabase(database)
|
||||
f = open(inputFile)
|
||||
try:
|
||||
fileLines = f.readlines()
|
||||
finally:
|
||||
f.close()
|
||||
inputLine = InputLine(inputUser, inputProduct, inputType)
|
||||
startDate = getDateFile(fileLines[0].partition("|")[2].partition(" ")[0], sdate)
|
||||
endDate = getDateFile(fileLines[-1].partition("|")[2].partition(" ")[0], edate)
|
||||
inputLine.numberOfDays = (endDate - startDate).days
|
||||
database = Database()
|
||||
database = clearDatabase(database)
|
||||
|
||||
if (inputType == 3) or (inputType == 4):
|
||||
database.globalDatoVarer = [0] * (inputLine.numberOfDays + 1)
|
||||
database.globalDatoForbruk = [0] * (inputLine.numberOfDays + 1)
|
||||
database.globalUkedagForbruk = [0] * 7
|
||||
database.pengebeholdning = [0] * (inputLine.numberOfDays + 1)
|
||||
for linje in fileLines:
|
||||
if not (linje[0] == "#") and not (linje == "\n"):
|
||||
# henter dateNum, products, user, price
|
||||
restDel = linje.partition("|")
|
||||
restDel = restDel[2].partition(" ")
|
||||
inputLine.dateNum, inputLine.weekday = dateToDateNumFile(restDel[0], startDate)
|
||||
if inputLine.dateNum < 0 or inputLine.dateNum > (inputLine.numberOfDays):
|
||||
continue
|
||||
restDel = restDel[2].partition("|")
|
||||
restDel = restDel[2].partition("|")
|
||||
products = restDel[0]
|
||||
restDel = restDel[2].partition("|")
|
||||
inputLine.user = restDel[0]
|
||||
inputLine.price = int(restDel[2].partition("|")[0])
|
||||
for inputLine.product in getProducts(products):
|
||||
database = addLineToDatabase(database, inputLine)
|
||||
inputLine.price = 0
|
||||
# bygg database.pengebeholdning
|
||||
if (inputType == 3) or (inputType == 4):
|
||||
for i in range(inputLine.numberOfDays + 1):
|
||||
if i > 0:
|
||||
database.pengebeholdning[i] += database.pengebeholdning[i - 1]
|
||||
# bygg dateLine
|
||||
day = datetime.timedelta(days=1)
|
||||
dateLine = []
|
||||
dateLine.append(startDate)
|
||||
for n in range(inputLine.numberOfDays):
|
||||
dateLine.append(startDate + n * day)
|
||||
return database, dateLine
|
||||
|
||||
if (inputType==3) or (inputType==4):
|
||||
database.globalDatoVarer = [0]*(inputLine.numberOfDays+1)
|
||||
database.globalDatoForbruk = [0]*(inputLine.numberOfDays+1)
|
||||
database.globalUkedagForbruk = [0]*7
|
||||
database.pengebeholdning = [0]*(inputLine.numberOfDays+1)
|
||||
for linje in fileLines:
|
||||
if not (linje[0]=='#') and not (linje=='\n') :
|
||||
#henter dateNum, products, user, price
|
||||
restDel = linje.partition('|')
|
||||
restDel = restDel[2].partition(' ')
|
||||
inputLine.dateNum, inputLine.weekday = dateToDateNumFile(restDel[0], startDate)
|
||||
if inputLine.dateNum<0 or inputLine.dateNum>(inputLine.numberOfDays):
|
||||
continue
|
||||
restDel=restDel[2].partition('|')
|
||||
restDel=restDel[2].partition('|')
|
||||
products = restDel[0]
|
||||
restDel=restDel[2].partition('|')
|
||||
inputLine.user=restDel[0]
|
||||
inputLine.price=int(restDel[2].partition('|')[0])
|
||||
for inputLine.product in getProducts(products):
|
||||
database=addLineToDatabase(database, inputLine )
|
||||
inputLine.price = 0;
|
||||
#bygg database.pengebeholdning
|
||||
if (inputType==3) or (inputType==4):
|
||||
for i in range(inputLine.numberOfDays+1):
|
||||
if i > 0:
|
||||
database.pengebeholdning[i] +=database.pengebeholdning[i-1]
|
||||
#bygg dateLine
|
||||
day=datetime.timedelta(days=1)
|
||||
dateLine=[]
|
||||
dateLine.append(startDate)
|
||||
for n in range(inputLine.numberOfDays):
|
||||
dateLine.append(startDate+n*day)
|
||||
return database, dateLine
|
||||
|
||||
def printTopDict(dictionary, n, k):
|
||||
i=0
|
||||
for key in sorted(dictionary, key=dictionary.get, reverse=k):
|
||||
print(key, ': ',dictionary[key])
|
||||
if i<n:
|
||||
i += 1
|
||||
else:
|
||||
break
|
||||
i = 0
|
||||
for key in sorted(dictionary, key=dictionary.get, reverse=k):
|
||||
print(key, ": ", dictionary[key])
|
||||
if i < n:
|
||||
i += 1
|
||||
else:
|
||||
break
|
||||
|
||||
|
||||
def printTopDict2(dictionary, dictionary2, n):
|
||||
print('')
|
||||
print('product : price[kr] ( number )')
|
||||
i=0
|
||||
for key in sorted(dictionary, key=dictionary.get, reverse=True):
|
||||
print(key, ': ',dictionary[key], ' (', dictionary2[key], ') ')
|
||||
if i<n:
|
||||
i += 1
|
||||
else:
|
||||
break
|
||||
print("")
|
||||
print("product : price[kr] ( number )")
|
||||
i = 0
|
||||
for key in sorted(dictionary, key=dictionary.get, reverse=True):
|
||||
print(key, ": ", dictionary[key], " (", dictionary2[key], ") ")
|
||||
if i < n:
|
||||
i += 1
|
||||
else:
|
||||
break
|
||||
|
||||
|
||||
def printWeekdays(week, days):
|
||||
if week==[] or days==0:
|
||||
return
|
||||
print('mon: ', '%.2f'%(week[0]*7.0/days), ' tue: ', '%.2f'%(week[1]*7.0/days), ' wen: ', '%.2f'%(week[2]*7.0/days), ' thu: ', '%.2f'%(week[3]*7.0/days), ' fri: ', '%.2f'%(week[4]*7.0/days), ' sat: ','%.2f'%( week[5]*7.0/days), ' sun: ', '%.2f'%(week[6]*7.0/days))
|
||||
print('forbruk per dag (snitt): ', '%.2f'%(sum(week)*1.0/days))
|
||||
print('')
|
||||
if week == [] or days == 0:
|
||||
return
|
||||
print(
|
||||
"mon: ",
|
||||
"%.2f" % (week[0] * 7.0 / days),
|
||||
" tue: ",
|
||||
"%.2f" % (week[1] * 7.0 / days),
|
||||
" wen: ",
|
||||
"%.2f" % (week[2] * 7.0 / days),
|
||||
" thu: ",
|
||||
"%.2f" % (week[3] * 7.0 / days),
|
||||
" fri: ",
|
||||
"%.2f" % (week[4] * 7.0 / days),
|
||||
" sat: ",
|
||||
"%.2f" % (week[5] * 7.0 / days),
|
||||
" sun: ",
|
||||
"%.2f" % (week[6] * 7.0 / days),
|
||||
)
|
||||
print("forbruk per dag (snitt): ", "%.2f" % (sum(week) * 1.0 / days))
|
||||
print("")
|
||||
|
||||
|
||||
def printBalance(database, user):
|
||||
forbruk = 0
|
||||
if (user in database.personVareVerdi):
|
||||
forbruk = sum([i for i in list(database.personVareVerdi[user].values())])
|
||||
print('totalt kjøpt for: ', forbruk, end=' ')
|
||||
if (user in database.personNegTransactions):
|
||||
print('kr, totalt lagt til: ', -database.personNegTransactions[user], end=' ')
|
||||
forbruk=-database.personNegTransactions[user]-forbruk
|
||||
if (user in database.personPosTransactions):
|
||||
print('kr, totalt tatt fra boks: ', database.personPosTransactions[user], end=' ')
|
||||
forbruk=forbruk-database.personPosTransactions[user]
|
||||
print('balanse: ', forbruk, 'kr', end=' ')
|
||||
print('')
|
||||
forbruk = 0
|
||||
if user in database.personVareVerdi:
|
||||
forbruk = sum([i for i in list(database.personVareVerdi[user].values())])
|
||||
print("totalt kjøpt for: ", forbruk, end=" ")
|
||||
if user in database.personNegTransactions:
|
||||
print("kr, totalt lagt til: ", -database.personNegTransactions[user], end=" ")
|
||||
forbruk = -database.personNegTransactions[user] - forbruk
|
||||
if user in database.personPosTransactions:
|
||||
print("kr, totalt tatt fra boks: ", database.personPosTransactions[user], end=" ")
|
||||
forbruk = forbruk - database.personPosTransactions[user]
|
||||
print("balanse: ", forbruk, "kr", end=" ")
|
||||
print("")
|
||||
|
||||
|
||||
def printUser(database, dateLine, user, n):
|
||||
printTopDict2(database.personVareVerdi[user], database.personVareAntall[user], n)
|
||||
print('\nforbruk per ukedag [kr/dag],', end=' ')
|
||||
printWeekdays(database.personUkedagVerdi[user], len(dateLine))
|
||||
printBalance(database, user)
|
||||
printTopDict2(database.personVareVerdi[user], database.personVareAntall[user], n)
|
||||
print("\nforbruk per ukedag [kr/dag],", end=" ")
|
||||
printWeekdays(database.personUkedagVerdi[user], len(dateLine))
|
||||
printBalance(database, user)
|
||||
|
||||
|
||||
def printProduct(database, dateLine, product, n):
|
||||
printTopDict(database.varePersonAntall[product], n, 1)
|
||||
print('\nforbruk per ukedag [antall/dag],', end=' ')
|
||||
printWeekdays(database.vareUkedagAntall[product], len(dateLine))
|
||||
print('Det er solgt: ', database.globalVareAntall[product], product, 'til en verdi av: ', database.globalVareVerdi[product], 'kr')
|
||||
printTopDict(database.varePersonAntall[product], n, 1)
|
||||
print("\nforbruk per ukedag [antall/dag],", end=" ")
|
||||
printWeekdays(database.vareUkedagAntall[product], len(dateLine))
|
||||
print(
|
||||
"Det er solgt: ",
|
||||
database.globalVareAntall[product],
|
||||
product,
|
||||
"til en verdi av: ",
|
||||
database.globalVareVerdi[product],
|
||||
"kr",
|
||||
)
|
||||
|
||||
|
||||
def printGlobal(database, dateLine, n):
|
||||
print('\nmest lagt til: ')
|
||||
printTopDict(database.personNegTransactions, n, 0)
|
||||
print('\nmest tatt fra:')
|
||||
printTopDict(database.personPosTransactions, n, 1)
|
||||
print('\nstørst forbruk:')
|
||||
printTopDict(database.globalPersonForbruk, n, 1)
|
||||
printTopDict2(database.globalVareVerdi, database.globalVareAntall, n)
|
||||
print('\nforbruk per ukedag [kr/dag],', end=' ')
|
||||
printWeekdays(database.globalUkedagForbruk, len(dateLine))
|
||||
print('Det er solgt varer til en verdi av: ', sum(database.globalDatoForbruk), 'kr, det er lagt til', -sum([i for i in list(database.personNegTransactions.values())]), 'og tatt fra', sum([i for i in list(database.personPosTransactions.values())]), end=' ')
|
||||
print('balansen blir:', database.pengebeholdning[len(dateLine)-1], 'der negative verdier representerer at brukere har kreditt tilgjengelig')
|
||||
print("\nmest lagt til: ")
|
||||
printTopDict(database.personNegTransactions, n, 0)
|
||||
print("\nmest tatt fra:")
|
||||
printTopDict(database.personPosTransactions, n, 1)
|
||||
print("\nstørst forbruk:")
|
||||
printTopDict(database.globalPersonForbruk, n, 1)
|
||||
printTopDict2(database.globalVareVerdi, database.globalVareAntall, n)
|
||||
print("\nforbruk per ukedag [kr/dag],", end=" ")
|
||||
printWeekdays(database.globalUkedagForbruk, len(dateLine))
|
||||
print(
|
||||
"Det er solgt varer til en verdi av: ",
|
||||
sum(database.globalDatoForbruk),
|
||||
"kr, det er lagt til",
|
||||
-sum([i for i in list(database.personNegTransactions.values())]),
|
||||
"og tatt fra",
|
||||
sum([i for i in list(database.personPosTransactions.values())]),
|
||||
end=" ",
|
||||
)
|
||||
print(
|
||||
"balansen blir:",
|
||||
database.pengebeholdning[len(dateLine) - 1],
|
||||
"der negative verdier representerer at brukere har kreditt tilgjengelig",
|
||||
)
|
||||
|
||||
|
||||
def alt4menuTextOnly(database, dateLine):
|
||||
n=10
|
||||
while 1:
|
||||
print('\n1: user-statistics, 2: product-statistics, 3:global-statistics, n: adjust amount of data shown q:quit')
|
||||
inp = input('')
|
||||
if inp == 'q':
|
||||
break
|
||||
elif inp == '1':
|
||||
try:
|
||||
printUser(database, dateLine, getUser(), n)
|
||||
except:
|
||||
print('\n\nSomething is not right, (last date prior to first date?)')
|
||||
elif inp == '2':
|
||||
try:
|
||||
printProduct(database, dateLine, getProduct(), n)
|
||||
except:
|
||||
print('\n\nSomething is not right, (last date prior to first date?)')
|
||||
elif inp == '3':
|
||||
try:
|
||||
printGlobal(database, dateLine, n)
|
||||
except:
|
||||
print('\n\nSomething is not right, (last date prior to first date?)')
|
||||
elif inp == 'n':
|
||||
n=int(input('set number to show '));
|
||||
n = 10
|
||||
while 1:
|
||||
print(
|
||||
"\n1: user-statistics, 2: product-statistics, 3:global-statistics, n: adjust amount of data shown q:quit"
|
||||
)
|
||||
inp = input("")
|
||||
if inp == "q":
|
||||
break
|
||||
elif inp == "1":
|
||||
try:
|
||||
printUser(database, dateLine, getUser(), n)
|
||||
except:
|
||||
print("\n\nSomething is not right, (last date prior to first date?)")
|
||||
elif inp == "2":
|
||||
try:
|
||||
printProduct(database, dateLine, getProduct(), n)
|
||||
except:
|
||||
print("\n\nSomething is not right, (last date prior to first date?)")
|
||||
elif inp == "3":
|
||||
try:
|
||||
printGlobal(database, dateLine, n)
|
||||
except:
|
||||
print("\n\nSomething is not right, (last date prior to first date?)")
|
||||
elif inp == "n":
|
||||
n = int(input("set number to show "))
|
||||
|
||||
|
||||
def statisticsTextOnly():
|
||||
inputType = 4
|
||||
product = ''
|
||||
user = ''
|
||||
print('\n0: from file, 1: from database, q:quit')
|
||||
inp = input('')
|
||||
if inp == '1':
|
||||
database, dateLine = buildDatabaseFromDb(inputType, product, user)
|
||||
elif inp=='0' or inp == '':
|
||||
database, dateLine = buildDatabaseFromFile('default.dibblerlog', inputType, product, user)
|
||||
if not inp == 'q':
|
||||
alt4menuTextOnly(database, dateLine)
|
||||
inputType = 4
|
||||
product = ""
|
||||
user = ""
|
||||
print("\n0: from file, 1: from database, q:quit")
|
||||
inp = input("")
|
||||
if inp == "1":
|
||||
database, dateLine = buildDatabaseFromDb(inputType, product, user)
|
||||
elif inp == "0" or inp == "":
|
||||
database, dateLine = buildDatabaseFromFile("default.dibblerlog", inputType, product, user)
|
||||
if not inp == "q":
|
||||
alt4menuTextOnly(database, dateLine)
|
||||
|
|
|
@ -13,38 +13,33 @@ parser.add_argument(
|
|||
)
|
||||
|
||||
subparsers = parser.add_subparsers(
|
||||
title='subcommands',
|
||||
dest='subcommand',
|
||||
title="subcommands",
|
||||
dest="subcommand",
|
||||
required=True,
|
||||
)
|
||||
subparsers.add_parser(
|
||||
'loop',
|
||||
help='Run the dibbler loop'
|
||||
)
|
||||
subparsers.add_parser(
|
||||
'create-db',
|
||||
help='Create the database'
|
||||
)
|
||||
subparsers.add_parser(
|
||||
'slabbedasker',
|
||||
help='Find out who is slabbedasker'
|
||||
)
|
||||
subparsers.add_parser("loop", help="Run the dibbler loop")
|
||||
subparsers.add_parser("create-db", help="Create the database")
|
||||
subparsers.add_parser("slabbedasker", help="Find out who is slabbedasker")
|
||||
|
||||
|
||||
def main():
|
||||
args = parser.parse_args()
|
||||
config.read(args.config)
|
||||
|
||||
if args.subcommand == 'loop':
|
||||
import dibbler.subcommands.loop as loop
|
||||
loop.main()
|
||||
if args.subcommand == "loop":
|
||||
import dibbler.subcommands.loop as loop
|
||||
|
||||
elif args.subcommand == 'create-db':
|
||||
import dibbler.subcommands.makedb as makedb
|
||||
makedb.main()
|
||||
loop.main()
|
||||
|
||||
elif args.subcommand == 'slabbedasker':
|
||||
import dibbler.subcommands.slabbedasker as slabbedasker
|
||||
slabbedasker.main()
|
||||
elif args.subcommand == "create-db":
|
||||
import dibbler.subcommands.makedb as makedb
|
||||
|
||||
makedb.main()
|
||||
|
||||
elif args.subcommand == "slabbedasker":
|
||||
import dibbler.subcommands.slabbedasker as slabbedasker
|
||||
|
||||
slabbedasker.main()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
@ -21,8 +21,8 @@ from .miscmenus import (
|
|||
)
|
||||
from .printermenu import PrintLabelMenu
|
||||
from .stats import (
|
||||
ProductPopularityMenu,
|
||||
ProductRevenueMenu,
|
||||
BalanceMenu,
|
||||
LoggedStatisticsMenu
|
||||
ProductPopularityMenu,
|
||||
ProductRevenueMenu,
|
||||
BalanceMenu,
|
||||
LoggedStatisticsMenu,
|
||||
)
|
||||
|
|
|
@ -14,10 +14,10 @@ from .helpermenus import Menu
|
|||
|
||||
class AddStockMenu(Menu):
|
||||
def __init__(self):
|
||||
Menu.__init__(self, 'Add stock and adjust credit', uses_db=True)
|
||||
self.help_text = '''
|
||||
Menu.__init__(self, "Add stock and adjust credit", uses_db=True)
|
||||
self.help_text = """
|
||||
Enter what you have bought for PVVVV here, along with your user name and how
|
||||
much money you're due in credits for the purchase when prompted.\n'''
|
||||
much money you're due in credits for the purchase when prompted.\n"""
|
||||
self.users = []
|
||||
self.users = []
|
||||
self.products = {}
|
||||
|
@ -25,10 +25,19 @@ much money you're due in credits for the purchase when prompted.\n'''
|
|||
|
||||
def _execute(self):
|
||||
questions = {
|
||||
(False, False): 'Enter user id or a string of the form "<number> <product>"',
|
||||
(False, True): 'Enter user id or more strings of the form "<number> <product>"',
|
||||
(
|
||||
False,
|
||||
False,
|
||||
): 'Enter user id or a string of the form "<number> <product>"',
|
||||
(
|
||||
False,
|
||||
True,
|
||||
): 'Enter user id or more strings of the form "<number> <product>"',
|
||||
(True, False): 'Enter a string of the form "<number> <product>"',
|
||||
(True, True): 'Enter more strings of the form "<number> <product>", or an empty line to confirm'
|
||||
(
|
||||
True,
|
||||
True,
|
||||
): 'Enter more strings of the form "<number> <product>", or an empty line to confirm',
|
||||
}
|
||||
|
||||
self.users = []
|
||||
|
@ -41,24 +50,33 @@ much money you're due in credits for the purchase when prompted.\n'''
|
|||
thing_price = 0
|
||||
|
||||
# Read in a 'thing' (product or user):
|
||||
line = self.input_multiple(add_nonexisting=('user', 'product'), empty_input_permitted=True,
|
||||
find_hidden_products=False)
|
||||
line = self.input_multiple(
|
||||
add_nonexisting=("user", "product"),
|
||||
empty_input_permitted=True,
|
||||
find_hidden_products=False,
|
||||
)
|
||||
|
||||
if line:
|
||||
(thing, amount) = line
|
||||
|
||||
if isinstance(thing, Product):
|
||||
self.printc(f"{amount:d} of {thing.name} registered")
|
||||
thing_price = self.input_int('What did you pay a piece?', 1, 100000, default=thing.price) * amount
|
||||
thing_price = (
|
||||
self.input_int("What did you pay a piece?", 1, 100000, default=thing.price)
|
||||
* amount
|
||||
)
|
||||
self.price += thing_price
|
||||
|
||||
# once we get something in the
|
||||
# purchase, we want to protect the
|
||||
# user from accidentally killing it
|
||||
self.exit_confirm_msg = 'Abort transaction?'
|
||||
self.exit_confirm_msg = "Abort transaction?"
|
||||
else:
|
||||
if not self.complete_input():
|
||||
if self.confirm('Not enough information entered. Abort transaction?', default=True):
|
||||
if self.confirm(
|
||||
"Not enough information entered. Abort transaction?",
|
||||
default=True,
|
||||
):
|
||||
return False
|
||||
continue
|
||||
break
|
||||
|
@ -74,7 +92,7 @@ much money you're due in credits for the purchase when prompted.\n'''
|
|||
def print_info(self):
|
||||
width = 6 + Product.name_length
|
||||
print()
|
||||
print(width * '-')
|
||||
print(width * "-")
|
||||
if self.price:
|
||||
print(f"Amount to be credited:{self.price:>{width - 22}}")
|
||||
if self.users:
|
||||
|
@ -84,41 +102,47 @@ much money you're due in credits for the purchase when prompted.\n'''
|
|||
print()
|
||||
print("Products", end="")
|
||||
print("Amount".rjust(width - 8))
|
||||
print(width * '-')
|
||||
print(width * "-")
|
||||
if len(self.products):
|
||||
for product in list(self.products.keys()):
|
||||
print(f"{product.name}", end="")
|
||||
print(f"{self.products[product][0]}".rjust(width - len(product.name)))
|
||||
print(width * '-')
|
||||
print(width * "-")
|
||||
|
||||
def add_thing_to_pending(self, thing, amount, price):
|
||||
if isinstance(thing, User):
|
||||
self.users.append(thing)
|
||||
elif thing in list(self.products.keys()):
|
||||
print('Already added this product, adding amounts')
|
||||
print("Already added this product, adding amounts")
|
||||
self.products[thing][0] += amount
|
||||
self.products[thing][1] += price
|
||||
else:
|
||||
self.products[thing] = [amount, price]
|
||||
|
||||
def perform_transaction(self):
|
||||
print('Did you pay a different price?')
|
||||
if self.confirm('>', default=False):
|
||||
self.price = self.input_int('How much did you pay?', 0, self.price, default=self.price)
|
||||
print("Did you pay a different price?")
|
||||
if self.confirm(">", default=False):
|
||||
self.price = self.input_int("How much did you pay?", 0, self.price, default=self.price)
|
||||
|
||||
description = self.input_str('Log message', length_range=(0, 50))
|
||||
if description == '':
|
||||
description = 'Purchased products for PVVVV, adjusted credit ' + str(self.price)
|
||||
description = self.input_str("Log message", length_range=(0, 50))
|
||||
if description == "":
|
||||
description = "Purchased products for PVVVV, adjusted credit " + str(self.price)
|
||||
for product in self.products:
|
||||
value = max(product.stock, 0) * product.price + self.products[product][1]
|
||||
old_price = product.price
|
||||
old_hidden = product.hidden
|
||||
product.price = int(ceil(float(value) / (max(product.stock, 0) + self.products[product][0])))
|
||||
product.stock = max(self.products[product][0], product.stock + self.products[product][0])
|
||||
product.price = int(
|
||||
ceil(float(value) / (max(product.stock, 0) + self.products[product][0]))
|
||||
)
|
||||
product.stock = max(
|
||||
self.products[product][0], product.stock + self.products[product][0]
|
||||
)
|
||||
product.hidden = False
|
||||
print(f"New stock for {product.name}: {product.stock:d}",
|
||||
f"- New price: {product.price}" if old_price != product.price else "",
|
||||
"- Removed hidden status" if old_hidden != product.hidden else "")
|
||||
print(
|
||||
f"New stock for {product.name}: {product.stock:d}",
|
||||
f"- New price: {product.price}" if old_price != product.price else "",
|
||||
"- Removed hidden status" if old_hidden != product.hidden else "",
|
||||
)
|
||||
|
||||
purchase = Purchase()
|
||||
for user in self.users:
|
||||
|
@ -136,4 +160,4 @@ much money you're due in credits for the purchase when prompted.\n'''
|
|||
for user in self.users:
|
||||
print(f"User {user.name}'s credit is now {user.credit:d}")
|
||||
except sqlalchemy.exc.SQLAlchemyError as e:
|
||||
print(f'Could not perform transaction: {e}')
|
||||
print(f"Could not perform transaction: {e}")
|
||||
|
|
|
@ -14,18 +14,18 @@ from .helpermenus import Menu
|
|||
|
||||
class BuyMenu(Menu):
|
||||
def __init__(self, session=None):
|
||||
Menu.__init__(self, 'Buy', uses_db=True)
|
||||
Menu.__init__(self, "Buy", uses_db=True)
|
||||
if session:
|
||||
self.session = session
|
||||
self.superfast_mode = False
|
||||
self.help_text = '''
|
||||
self.help_text = """
|
||||
Each purchase may contain one or more products and one or more buyers.
|
||||
|
||||
Enter products (by name or bar code) and buyers (by name or bar code)
|
||||
in any order. The information gathered so far is displayed after each
|
||||
addition, and you can type 'what' at any time to redisplay it.
|
||||
|
||||
When finished, write an empty line to confirm the purchase.\n'''
|
||||
When finished, write an empty line to confirm the purchase.\n"""
|
||||
|
||||
@staticmethod
|
||||
def credit_check(user):
|
||||
|
@ -37,7 +37,7 @@ When finished, write an empty line to confirm the purchase.\n'''
|
|||
"""
|
||||
assert isinstance(user, User)
|
||||
|
||||
return user.credit > config.getint('limits', 'low_credit_warning_limit')
|
||||
return user.credit > config.getint("limits", "low_credit_warning_limit")
|
||||
|
||||
def low_credit_warning(self, user, timeout=False):
|
||||
assert isinstance(user, User)
|
||||
|
@ -57,7 +57,9 @@ When finished, write an empty line to confirm the purchase.\n'''
|
|||
print("***********************************************************************")
|
||||
print("***********************************************************************")
|
||||
print("")
|
||||
print(f"USER {user.name} HAS LOWER CREDIT THAN {config.getint('limits', 'low_credit_warning_limit'):d}.")
|
||||
print(
|
||||
f"USER {user.name} HAS LOWER CREDIT THAN {config.getint('limits', 'low_credit_warning_limit'):d}."
|
||||
)
|
||||
print("THIS PURCHASE WILL CHARGE YOUR CREDIT TWICE AS MUCH.")
|
||||
print("CONSIDER PUTTING MONEY IN THE BOX TO AVOID THIS.")
|
||||
print("")
|
||||
|
@ -72,10 +74,10 @@ When finished, write an empty line to confirm the purchase.\n'''
|
|||
def add_thing_to_purchase(self, thing, amount=1):
|
||||
if isinstance(thing, User):
|
||||
if thing.is_anonymous():
|
||||
print('---------------------------------------------')
|
||||
print('| You are now purchasing as the user anonym.|')
|
||||
print('| You have to put money in the anonym-jar. |')
|
||||
print('---------------------------------------------')
|
||||
print("---------------------------------------------")
|
||||
print("| You are now purchasing as the user anonym.|")
|
||||
print("| You have to put money in the anonym-jar. |")
|
||||
print("---------------------------------------------")
|
||||
|
||||
if not self.credit_check(thing):
|
||||
if self.low_credit_warning(user=thing, timeout=self.superfast_mode):
|
||||
|
@ -110,24 +112,32 @@ When finished, write an empty line to confirm the purchase.\n'''
|
|||
|
||||
if len(initial_contents) > 0 and all(map(is_product, initial_contents)):
|
||||
self.superfast_mode = True
|
||||
print('***********************************************')
|
||||
print('****** Buy menu is in SUPERFASTmode[tm]! ******')
|
||||
print('*** The purchase will be stored immediately ***')
|
||||
print('*** when you enter a user. ***')
|
||||
print('***********************************************')
|
||||
print("***********************************************")
|
||||
print("****** Buy menu is in SUPERFASTmode[tm]! ******")
|
||||
print("*** The purchase will be stored immediately ***")
|
||||
print("*** when you enter a user. ***")
|
||||
print("***********************************************")
|
||||
|
||||
while True:
|
||||
self.print_purchase()
|
||||
self.printc({(False, False): 'Enter user or product identification',
|
||||
(False, True): 'Enter user identification or more products',
|
||||
(True, False): 'Enter product identification or more users',
|
||||
(True, True): 'Enter more products or users, or an empty line to confirm'
|
||||
}[(len(self.purchase.transactions) > 0,
|
||||
len(self.purchase.entries) > 0)])
|
||||
self.printc(
|
||||
{
|
||||
(False, False): "Enter user or product identification",
|
||||
(False, True): "Enter user identification or more products",
|
||||
(True, False): "Enter product identification or more users",
|
||||
(
|
||||
True,
|
||||
True,
|
||||
): "Enter more products or users, or an empty line to confirm",
|
||||
}[(len(self.purchase.transactions) > 0, len(self.purchase.entries) > 0)]
|
||||
)
|
||||
|
||||
# Read in a 'thing' (product or user):
|
||||
line = self.input_multiple(add_nonexisting=('user', 'product'), empty_input_permitted=True,
|
||||
find_hidden_products=False)
|
||||
line = self.input_multiple(
|
||||
add_nonexisting=("user", "product"),
|
||||
empty_input_permitted=True,
|
||||
find_hidden_products=False,
|
||||
)
|
||||
if line is not None:
|
||||
thing, num = line
|
||||
else:
|
||||
|
@ -136,7 +146,9 @@ When finished, write an empty line to confirm the purchase.\n'''
|
|||
# Possibly exit from the menu:
|
||||
if thing is None:
|
||||
if not self.complete_input():
|
||||
if self.confirm('Not enough information entered. Abort purchase?', default=True):
|
||||
if self.confirm(
|
||||
"Not enough information entered. Abort purchase?", default=True
|
||||
):
|
||||
return False
|
||||
continue
|
||||
break
|
||||
|
@ -144,7 +156,7 @@ When finished, write an empty line to confirm the purchase.\n'''
|
|||
# once we get something in the
|
||||
# purchase, we want to protect the
|
||||
# user from accidentally killing it
|
||||
self.exit_confirm_msg = 'Abort purchase?'
|
||||
self.exit_confirm_msg = "Abort purchase?"
|
||||
|
||||
# Add the thing to our purchase object:
|
||||
if not self.add_thing_to_purchase(thing, amount=num):
|
||||
|
@ -159,20 +171,22 @@ When finished, write an empty line to confirm the purchase.\n'''
|
|||
try:
|
||||
self.session.commit()
|
||||
except sqlalchemy.exc.SQLAlchemyError as e:
|
||||
print(f'Could not store purchase: {e}')
|
||||
print(f"Could not store purchase: {e}")
|
||||
else:
|
||||
print('Purchase stored.')
|
||||
print("Purchase stored.")
|
||||
self.print_purchase()
|
||||
for t in self.purchase.transactions:
|
||||
if not t.user.is_anonymous():
|
||||
print(f"User {t.user.name}'s credit is now {t.user.credit:d} kr")
|
||||
if t.user.credit < config.getint('limits', 'low_credit_warning_limit'):
|
||||
print(f'USER {t.user.name} HAS LOWER CREDIT THAN {config.getint("limits", "low_credit_warning_limit"):d},',
|
||||
'AND SHOULD CONSIDER PUTTING SOME MONEY IN THE BOX.')
|
||||
if t.user.credit < config.getint("limits", "low_credit_warning_limit"):
|
||||
print(
|
||||
f'USER {t.user.name} HAS LOWER CREDIT THAN {config.getint("limits", "low_credit_warning_limit"):d},',
|
||||
"AND SHOULD CONSIDER PUTTING SOME MONEY IN THE BOX.",
|
||||
)
|
||||
|
||||
# Superfast mode skips a linebreak for some reason.
|
||||
if self.superfast_mode:
|
||||
print("")
|
||||
print("")
|
||||
return True
|
||||
|
||||
def complete_input(self):
|
||||
|
@ -184,30 +198,33 @@ When finished, write an empty line to confirm the purchase.\n'''
|
|||
entries = self.purchase.entries
|
||||
if len(transactions) == 0 and len(entries) == 0:
|
||||
return None
|
||||
string = 'Purchase:'
|
||||
string += '\n buyers: '
|
||||
string = "Purchase:"
|
||||
string += "\n buyers: "
|
||||
if len(transactions) == 0:
|
||||
string += '(empty)'
|
||||
string += "(empty)"
|
||||
else:
|
||||
string += ', '.join(
|
||||
[t.user.name + ("*" if not self.credit_check(t.user) else "") for t in transactions])
|
||||
string += '\n products: '
|
||||
string += ", ".join(
|
||||
[t.user.name + ("*" if not self.credit_check(t.user) else "") for t in transactions]
|
||||
)
|
||||
string += "\n products: "
|
||||
if len(entries) == 0:
|
||||
string += '(empty)'
|
||||
string += "(empty)"
|
||||
else:
|
||||
string += "\n "
|
||||
string += '\n '.join([f'{e.amount:d}x {e.product.name} ({e.product.price:d} kr)' for e in entries])
|
||||
string += "\n ".join(
|
||||
[f"{e.amount:d}x {e.product.name} ({e.product.price:d} kr)" for e in entries]
|
||||
)
|
||||
if len(transactions) > 1:
|
||||
string += f'\n price per person: {self.purchase.price_per_transaction():d} kr'
|
||||
string += f"\n price per person: {self.purchase.price_per_transaction():d} kr"
|
||||
if any(t.penalty > 1 for t in transactions):
|
||||
# TODO: Use penalty multiplier instead of 2
|
||||
string += f' *({self.purchase.price_per_transaction() * 2:d} kr)'
|
||||
string += f" *({self.purchase.price_per_transaction() * 2:d} kr)"
|
||||
|
||||
string += f'\n total price: {self.purchase.price:d} kr'
|
||||
string += f"\n total price: {self.purchase.price:d} kr"
|
||||
|
||||
if any(t.penalty > 1 for t in transactions):
|
||||
total = sum(self.purchase.price_per_transaction() * t.penalty for t in transactions)
|
||||
string += f'\n *total with penalty: {total} kr'
|
||||
string += f"\n *total with penalty: {total} kr"
|
||||
|
||||
return string
|
||||
|
||||
|
|
|
@ -3,155 +3,184 @@ import sqlalchemy
|
|||
from dibbler.models import User, Product
|
||||
from .helpermenus import Menu, Selector
|
||||
|
||||
__all__ = ["AddUserMenu", "AddProductMenu", "EditProductMenu", "AdjustStockMenu", "CleanupStockMenu", "EditUserMenu"]
|
||||
__all__ = [
|
||||
"AddUserMenu",
|
||||
"AddProductMenu",
|
||||
"EditProductMenu",
|
||||
"AdjustStockMenu",
|
||||
"CleanupStockMenu",
|
||||
"EditUserMenu",
|
||||
]
|
||||
|
||||
|
||||
class AddUserMenu(Menu):
|
||||
def __init__(self):
|
||||
Menu.__init__(self, 'Add user', uses_db=True)
|
||||
Menu.__init__(self, "Add user", uses_db=True)
|
||||
|
||||
def _execute(self):
|
||||
self.print_header()
|
||||
username = self.input_str('Username (should be same as PVV username)', regex=User.name_re, length_range=(1, 10))
|
||||
cardnum = self.input_str('Card number (optional)', regex=User.card_re, length_range=(0, 10))
|
||||
username = self.input_str(
|
||||
"Username (should be same as PVV username)",
|
||||
regex=User.name_re,
|
||||
length_range=(1, 10),
|
||||
)
|
||||
cardnum = self.input_str("Card number (optional)", regex=User.card_re, length_range=(0, 10))
|
||||
cardnum = cardnum.lower()
|
||||
rfid = self.input_str('RFID (optional)', regex=User.rfid_re, length_range=(0, 10))
|
||||
rfid = self.input_str("RFID (optional)", regex=User.rfid_re, length_range=(0, 10))
|
||||
user = User(username, cardnum, rfid)
|
||||
self.session.add(user)
|
||||
try:
|
||||
self.session.commit()
|
||||
print(f'User {username} stored')
|
||||
print(f"User {username} stored")
|
||||
except sqlalchemy.exc.IntegrityError as e:
|
||||
print(f'Could not store user {username}: {e}')
|
||||
print(f"Could not store user {username}: {e}")
|
||||
self.pause()
|
||||
|
||||
|
||||
class EditUserMenu(Menu):
|
||||
def __init__(self):
|
||||
Menu.__init__(self, 'Edit user', uses_db=True)
|
||||
self.help_text = '''
|
||||
Menu.__init__(self, "Edit user", uses_db=True)
|
||||
self.help_text = """
|
||||
The only editable part of a user is its card number and rfid.
|
||||
|
||||
First select an existing user, then enter a new card number for that
|
||||
user, then rfid (write an empty line to remove the card number or rfid).
|
||||
'''
|
||||
"""
|
||||
|
||||
def _execute(self):
|
||||
self.print_header()
|
||||
user = self.input_user('User')
|
||||
self.printc(f'Editing user {user.name}')
|
||||
card_str = f'"{user.card}"' if user.card is not None else 'empty'
|
||||
user.card = self.input_str(f'Card number (currently {card_str})',
|
||||
regex=User.card_re, length_range=(0, 10),
|
||||
empty_string_is_none=True)
|
||||
user = self.input_user("User")
|
||||
self.printc(f"Editing user {user.name}")
|
||||
card_str = f'"{user.card}"' if user.card is not None else "empty"
|
||||
user.card = self.input_str(
|
||||
f"Card number (currently {card_str})",
|
||||
regex=User.card_re,
|
||||
length_range=(0, 10),
|
||||
empty_string_is_none=True,
|
||||
)
|
||||
if user.card:
|
||||
user.card = user.card.lower()
|
||||
|
||||
rfid_str = f'"{user.rfid}"' if user.rfid is not None else 'empty'
|
||||
user.rfid = self.input_str(f'RFID (currently {rfid_str})',
|
||||
regex=User.rfid_re, length_range=(0, 10),
|
||||
empty_string_is_none=True)
|
||||
rfid_str = f'"{user.rfid}"' if user.rfid is not None else "empty"
|
||||
user.rfid = self.input_str(
|
||||
f"RFID (currently {rfid_str})",
|
||||
regex=User.rfid_re,
|
||||
length_range=(0, 10),
|
||||
empty_string_is_none=True,
|
||||
)
|
||||
try:
|
||||
self.session.commit()
|
||||
print(f'User {user.name} stored')
|
||||
print(f"User {user.name} stored")
|
||||
except sqlalchemy.exc.SQLAlchemyError as e:
|
||||
print(f'Could not store user {user.name}: {e}')
|
||||
print(f"Could not store user {user.name}: {e}")
|
||||
self.pause()
|
||||
|
||||
|
||||
class AddProductMenu(Menu):
|
||||
def __init__(self):
|
||||
Menu.__init__(self, 'Add product', uses_db=True)
|
||||
Menu.__init__(self, "Add product", uses_db=True)
|
||||
|
||||
def _execute(self):
|
||||
self.print_header()
|
||||
bar_code = self.input_str('Bar code', regex=Product.bar_code_re, length_range=(8, 13))
|
||||
name = self.input_str('Name', regex=Product.name_re, length_range=(1, Product.name_length))
|
||||
price = self.input_int('Price', 1, 100000)
|
||||
bar_code = self.input_str("Bar code", regex=Product.bar_code_re, length_range=(8, 13))
|
||||
name = self.input_str("Name", regex=Product.name_re, length_range=(1, Product.name_length))
|
||||
price = self.input_int("Price", 1, 100000)
|
||||
product = Product(bar_code, name, price)
|
||||
self.session.add(product)
|
||||
try:
|
||||
self.session.commit()
|
||||
print(f'Product {name} stored')
|
||||
print(f"Product {name} stored")
|
||||
except sqlalchemy.exc.SQLAlchemyError as e:
|
||||
print(f'Could not store product {name}: {e}')
|
||||
print(f"Could not store product {name}: {e}")
|
||||
self.pause()
|
||||
|
||||
|
||||
class EditProductMenu(Menu):
|
||||
def __init__(self):
|
||||
Menu.__init__(self, 'Edit product', uses_db=True)
|
||||
Menu.__init__(self, "Edit product", uses_db=True)
|
||||
|
||||
def _execute(self):
|
||||
self.print_header()
|
||||
product = self.input_product('Product')
|
||||
self.printc(f'Editing product {product.name}')
|
||||
product = self.input_product("Product")
|
||||
self.printc(f"Editing product {product.name}")
|
||||
while True:
|
||||
selector = Selector(f'Do what with {product.name}?',
|
||||
items=[('name', 'Edit name'),
|
||||
('price', 'Edit price'),
|
||||
('barcode', 'Edit barcode'),
|
||||
('hidden', 'Edit hidden status'),
|
||||
('store', 'Store')])
|
||||
selector = Selector(
|
||||
f"Do what with {product.name}?",
|
||||
items=[
|
||||
("name", "Edit name"),
|
||||
("price", "Edit price"),
|
||||
("barcode", "Edit barcode"),
|
||||
("hidden", "Edit hidden status"),
|
||||
("store", "Store"),
|
||||
],
|
||||
)
|
||||
what = selector.execute()
|
||||
if what == 'name':
|
||||
product.name = self.input_str('Name', default=product.name, regex=Product.name_re,
|
||||
length_range=(1, product.name_length))
|
||||
elif what == 'price':
|
||||
product.price = self.input_int('Price', 1, 100000, default=product.price)
|
||||
elif what == 'barcode':
|
||||
product.bar_code = self.input_str('Bar code', default=product.bar_code, regex=Product.bar_code_re,
|
||||
length_range=(8, 13))
|
||||
elif what == 'hidden':
|
||||
product.hidden = self.confirm(f'Hidden(currently {product.hidden})', default=False)
|
||||
elif what == 'store':
|
||||
if what == "name":
|
||||
product.name = self.input_str(
|
||||
"Name",
|
||||
default=product.name,
|
||||
regex=Product.name_re,
|
||||
length_range=(1, product.name_length),
|
||||
)
|
||||
elif what == "price":
|
||||
product.price = self.input_int("Price", 1, 100000, default=product.price)
|
||||
elif what == "barcode":
|
||||
product.bar_code = self.input_str(
|
||||
"Bar code",
|
||||
default=product.bar_code,
|
||||
regex=Product.bar_code_re,
|
||||
length_range=(8, 13),
|
||||
)
|
||||
elif what == "hidden":
|
||||
product.hidden = self.confirm(f"Hidden(currently {product.hidden})", default=False)
|
||||
elif what == "store":
|
||||
try:
|
||||
self.session.commit()
|
||||
print(f'Product {product.name} stored')
|
||||
print(f"Product {product.name} stored")
|
||||
except sqlalchemy.exc.SQLAlchemyError as e:
|
||||
print(f'Could not store product {product.name}: {e}')
|
||||
print(f"Could not store product {product.name}: {e}")
|
||||
self.pause()
|
||||
return
|
||||
elif what is None:
|
||||
print('Edit aborted')
|
||||
print("Edit aborted")
|
||||
return
|
||||
else:
|
||||
print('What what?')
|
||||
print("What what?")
|
||||
|
||||
|
||||
class AdjustStockMenu(Menu):
|
||||
def __init__(self):
|
||||
Menu.__init__(self, 'Adjust stock', uses_db=True)
|
||||
Menu.__init__(self, "Adjust stock", uses_db=True)
|
||||
|
||||
def _execute(self):
|
||||
self.print_header()
|
||||
product = self.input_product('Product')
|
||||
product = self.input_product("Product")
|
||||
|
||||
print(f'The stock of this product is: {product.stock:d}')
|
||||
print('Write the number of products you have added to the stock')
|
||||
print('Alternatively, correct the stock for any mistakes')
|
||||
add_stock = self.input_int('Added stock', -1000, 1000, zero_allowed=False)
|
||||
print(f"The stock of this product is: {product.stock:d}")
|
||||
print("Write the number of products you have added to the stock")
|
||||
print("Alternatively, correct the stock for any mistakes")
|
||||
add_stock = self.input_int("Added stock", -1000, 1000, zero_allowed=False)
|
||||
if add_stock > 0:
|
||||
print(f'You added {add_stock:d} to the stock of {product}')
|
||||
print(f"You added {add_stock:d} to the stock of {product}")
|
||||
else:
|
||||
print(f'You removed {add_stock:d} from the stock of {product}')
|
||||
print(f"You removed {add_stock:d} from the stock of {product}")
|
||||
|
||||
product.stock += add_stock
|
||||
|
||||
try:
|
||||
self.session.commit()
|
||||
print('Stock is now stored')
|
||||
print("Stock is now stored")
|
||||
self.pause()
|
||||
except sqlalchemy.exc.SQLAlchemyError as e:
|
||||
print(f'Could not store stock: {e}')
|
||||
print(f"Could not store stock: {e}")
|
||||
self.pause()
|
||||
return
|
||||
print(f'The stock is now {product.stock:d}')
|
||||
print(f"The stock is now {product.stock:d}")
|
||||
|
||||
|
||||
class CleanupStockMenu(Menu):
|
||||
def __init__(self):
|
||||
Menu.__init__(self, 'Stock Cleanup', uses_db=True)
|
||||
Menu.__init__(self, "Stock Cleanup", uses_db=True)
|
||||
|
||||
def _execute(self):
|
||||
self.print_header()
|
||||
|
@ -176,10 +205,10 @@ class CleanupStockMenu(Menu):
|
|||
|
||||
try:
|
||||
self.session.commit()
|
||||
print('New stocks are now stored.')
|
||||
print("New stocks are now stored.")
|
||||
self.pause()
|
||||
except sqlalchemy.exc.SQLAlchemyError as e:
|
||||
print(f'Could not store stock: {e}')
|
||||
print(f"Could not store stock: {e}")
|
||||
self.pause()
|
||||
return
|
||||
|
||||
|
|
|
@ -5,9 +5,11 @@ from .helpermenus import MessageMenu, Menu
|
|||
|
||||
class FAQMenu(Menu):
|
||||
def __init__(self):
|
||||
Menu.__init__(self, 'Frequently Asked Questions')
|
||||
self.items = [MessageMenu('What is the meaning with this program?',
|
||||
'''
|
||||
Menu.__init__(self, "Frequently Asked Questions")
|
||||
self.items = [
|
||||
MessageMenu(
|
||||
"What is the meaning with this program?",
|
||||
"""
|
||||
We want to avoid keeping lots of cash in PVVVV's money box and to
|
||||
make it easy to pay for stuff without using money. (Without using
|
||||
money each time, that is. You do of course have to pay for the things
|
||||
|
@ -19,15 +21,18 @@ class FAQMenu(Menu):
|
|||
stock and adjust credit".
|
||||
Alternatively, add money to the money box and use "Adjust credit" to
|
||||
tell Dibbler about it.
|
||||
'''),
|
||||
MessageMenu('Can I still pay for stuff using cash?',
|
||||
'''
|
||||
""",
|
||||
),
|
||||
MessageMenu(
|
||||
"Can I still pay for stuff using cash?",
|
||||
"""
|
||||
Please put money in the money box and use "Adjust Credit" so that
|
||||
dibbler can keep track of credit and purchases.'''),
|
||||
MessageMenu('How do I exit from a submenu/dialog/thing?',
|
||||
'Type "exit", "q", or ^d.'),
|
||||
MessageMenu('What does "." mean?',
|
||||
'''
|
||||
dibbler can keep track of credit and purchases.""",
|
||||
),
|
||||
MessageMenu("How do I exit from a submenu/dialog/thing?", 'Type "exit", "q", or ^d.'),
|
||||
MessageMenu(
|
||||
'What does "." mean?',
|
||||
"""
|
||||
The "." character, known as "full stop" or "period", is most often
|
||||
used to indicate the end of a sentence.
|
||||
|
||||
|
@ -35,29 +40,41 @@ class FAQMenu(Menu):
|
|||
read some text before continuing. Whenever some output ends with a
|
||||
line containing only a period, you should read the lines above and
|
||||
then press enter to continue.
|
||||
'''),
|
||||
MessageMenu('Why is the user interface so terribly unintuitive?',
|
||||
'''
|
||||
""",
|
||||
),
|
||||
MessageMenu(
|
||||
"Why is the user interface so terribly unintuitive?",
|
||||
"""
|
||||
Answer #1: It is not.
|
||||
|
||||
Answer #2: We are trying to compete with PVV's microwave oven in
|
||||
userfriendliness.
|
||||
|
||||
Answer #3: YOU are unintuitive.
|
||||
'''),
|
||||
MessageMenu('Why is there no help command?',
|
||||
'There is. Have you tried typing "help"?'),
|
||||
MessageMenu('Where are the easter eggs? I tried saying "moo", but nothing happened.',
|
||||
'Don\'t say "moo".'),
|
||||
MessageMenu('Why does the program speak English when all the users are Norwegians?',
|
||||
'Godt spørsmål. Det virket sikkert som en god idé der og da.'),
|
||||
MessageMenu('Why does the screen have strange colours?',
|
||||
'''
|
||||
""",
|
||||
),
|
||||
MessageMenu(
|
||||
"Why is there no help command?",
|
||||
'There is. Have you tried typing "help"?',
|
||||
),
|
||||
MessageMenu(
|
||||
'Where are the easter eggs? I tried saying "moo", but nothing happened.',
|
||||
'Don\'t say "moo".',
|
||||
),
|
||||
MessageMenu(
|
||||
"Why does the program speak English when all the users are Norwegians?",
|
||||
"Godt spørsmål. Det virket sikkert som en god idé der og da.",
|
||||
),
|
||||
MessageMenu(
|
||||
"Why does the screen have strange colours?",
|
||||
"""
|
||||
Type "c" on the main menu to change the colours of the display, or
|
||||
"cs" if you are a boring person.
|
||||
'''),
|
||||
MessageMenu('I found a bug; is there a reward?',
|
||||
'''
|
||||
""",
|
||||
),
|
||||
MessageMenu(
|
||||
"I found a bug; is there a reward?",
|
||||
"""
|
||||
No.
|
||||
|
||||
But if you are certain that it is a bug, not a feature, then you
|
||||
|
@ -83,9 +100,11 @@ class FAQMenu(Menu):
|
|||
|
||||
6. Type "restart" in Dibbler to replace the running process by a new
|
||||
one using the updated files.
|
||||
'''),
|
||||
MessageMenu('My question isn\'t listed here; what do I do?',
|
||||
'''
|
||||
""",
|
||||
),
|
||||
MessageMenu(
|
||||
"My question isn't listed here; what do I do?",
|
||||
"""
|
||||
DON'T PANIC.
|
||||
|
||||
Follow this procedure:
|
||||
|
@ -105,4 +124,6 @@ class FAQMenu(Menu):
|
|||
|
||||
5. Type "restart" in Dibbler to replace the running process by a new
|
||||
one using the updated files.
|
||||
''')]
|
||||
""",
|
||||
),
|
||||
]
|
||||
|
|
|
@ -14,10 +14,10 @@ from dibbler.lib.helpers import (
|
|||
argmax,
|
||||
)
|
||||
|
||||
exit_commands = ['exit', 'abort', 'quit', 'bye', 'eat flaming death', 'q']
|
||||
help_commands = ['help', '?']
|
||||
context_commands = ['what', '??']
|
||||
local_help_commands = ['help!', '???']
|
||||
exit_commands = ["exit", "abort", "quit", "bye", "eat flaming death", "q"]
|
||||
help_commands = ["help", "?"]
|
||||
context_commands = ["what", "??"]
|
||||
local_help_commands = ["help!", "???"]
|
||||
|
||||
|
||||
class ExitMenu(Exception):
|
||||
|
@ -25,10 +25,19 @@ class ExitMenu(Exception):
|
|||
|
||||
|
||||
class Menu(object):
|
||||
def __init__(self, name, items=None, prompt=None, end_prompt="> ",
|
||||
return_index=True,
|
||||
exit_msg=None, exit_confirm_msg=None, exit_disallowed_msg=None,
|
||||
help_text=None, uses_db=False):
|
||||
def __init__(
|
||||
self,
|
||||
name,
|
||||
items=None,
|
||||
prompt=None,
|
||||
end_prompt="> ",
|
||||
return_index=True,
|
||||
exit_msg=None,
|
||||
exit_confirm_msg=None,
|
||||
exit_disallowed_msg=None,
|
||||
help_text=None,
|
||||
uses_db=False,
|
||||
):
|
||||
self.name = name
|
||||
self.items = items if items is not None else []
|
||||
self.prompt = prompt
|
||||
|
@ -68,7 +77,7 @@ class Menu(object):
|
|||
if self.context is None:
|
||||
self.context = string
|
||||
else:
|
||||
self.context += '\n' + string
|
||||
self.context += "\n" + string
|
||||
|
||||
def show_context(self):
|
||||
print(self.header())
|
||||
|
@ -93,8 +102,16 @@ class Menu(object):
|
|||
return i
|
||||
return self.items[i]
|
||||
|
||||
def input_str(self, prompt=None, end_prompt=None, regex=None, length_range=(None, None),
|
||||
empty_string_is_none=False, timeout=None, default=None):
|
||||
def input_str(
|
||||
self,
|
||||
prompt=None,
|
||||
end_prompt=None,
|
||||
regex=None,
|
||||
length_range=(None, None),
|
||||
empty_string_is_none=False,
|
||||
timeout=None,
|
||||
default=None,
|
||||
):
|
||||
if prompt is None:
|
||||
prompt = self.prompt if self.prompt is not None else ""
|
||||
if default is not None:
|
||||
|
@ -114,13 +131,13 @@ class Menu(object):
|
|||
rlist, _, _ = select([sys.stdin], [], [], timeout)
|
||||
if not rlist:
|
||||
# timeout occurred, simulate empty line
|
||||
result = ''
|
||||
result = ""
|
||||
else:
|
||||
result = input(prompt).strip()
|
||||
else:
|
||||
result = input(prompt).strip()
|
||||
except EOFError:
|
||||
print('quit')
|
||||
print("quit")
|
||||
self.exit_menu()
|
||||
continue
|
||||
if result in exit_commands:
|
||||
|
@ -137,22 +154,26 @@ class Menu(object):
|
|||
continue
|
||||
if self.special_input_options(result):
|
||||
continue
|
||||
if empty_string_is_none and result == '':
|
||||
if empty_string_is_none and result == "":
|
||||
return None
|
||||
if default is not None and result == '':
|
||||
if default is not None and result == "":
|
||||
return default
|
||||
if regex is not None and not re.match(regex + '$', result):
|
||||
if regex is not None and not re.match(regex + "$", result):
|
||||
print(f'Value must match regular expression "{regex}"')
|
||||
continue
|
||||
if length_range != (None, None):
|
||||
length = len(result)
|
||||
if ((length_range[0] and length < length_range[0]) or (length_range[1] and length > length_range[1])):
|
||||
if (length_range[0] and length < length_range[0]) or (
|
||||
length_range[1] and length > length_range[1]
|
||||
):
|
||||
if length_range[0] and length_range[1]:
|
||||
print(f'Value must have length in range [{length_range[0]:d}, {length_range[1]:d}]')
|
||||
print(
|
||||
f"Value must have length in range [{length_range[0]:d}, {length_range[1]:d}]"
|
||||
)
|
||||
elif length_range[0]:
|
||||
print(f'Value must have length at least {length_range[0]:d}')
|
||||
print(f"Value must have length at least {length_range[0]:d}")
|
||||
else:
|
||||
print(f'Value must have length at most {length_range[1]:d}')
|
||||
print(f"Value must have length at most {length_range[1]:d}")
|
||||
continue
|
||||
return result
|
||||
|
||||
|
@ -179,8 +200,8 @@ class Menu(object):
|
|||
def input_choice(self, number_of_choices, prompt=None, end_prompt=None):
|
||||
while True:
|
||||
result = self.input_str(prompt, end_prompt)
|
||||
if result == '':
|
||||
print('Please enter something')
|
||||
if result == "":
|
||||
print("Please enter something")
|
||||
else:
|
||||
if result.isdigit():
|
||||
choice = int(result)
|
||||
|
@ -192,9 +213,17 @@ class Menu(object):
|
|||
self.invalid_menu_choice(result)
|
||||
|
||||
def invalid_menu_choice(self, in_str):
|
||||
print('Please enter a valid choice.')
|
||||
print("Please enter a valid choice.")
|
||||
|
||||
def input_int(self, prompt=None, minimum=None, maximum=None, null_allowed=False, zero_allowed=True, default=None):
|
||||
def input_int(
|
||||
self,
|
||||
prompt=None,
|
||||
minimum=None,
|
||||
maximum=None,
|
||||
null_allowed=False,
|
||||
zero_allowed=True,
|
||||
default=None,
|
||||
):
|
||||
if minimum is not None and maximum is not None:
|
||||
end_prompt = f"({minimum}-{maximum})>"
|
||||
elif minimum is not None:
|
||||
|
@ -206,15 +235,15 @@ class Menu(object):
|
|||
|
||||
while True:
|
||||
result = self.input_str(prompt + end_prompt, default=default)
|
||||
if result == '' and null_allowed:
|
||||
if result == "" and null_allowed:
|
||||
return False
|
||||
try:
|
||||
value = int(result)
|
||||
if minimum is not None and value < minimum:
|
||||
print(f'Value must be at least {minimum:d}')
|
||||
print(f"Value must be at least {minimum:d}")
|
||||
continue
|
||||
if maximum is not None and value > maximum:
|
||||
print(f'Value must be at most {maximum:d}')
|
||||
print(f"Value must be at most {maximum:d}")
|
||||
continue
|
||||
if not zero_allowed and value == 0:
|
||||
print("Value cannot be zero")
|
||||
|
@ -230,7 +259,7 @@ class Menu(object):
|
|||
return user
|
||||
|
||||
def retrieve_user(self, search_str):
|
||||
return self.search_ui(search_user, search_str, 'user')
|
||||
return self.search_ui(search_user, search_str, "user")
|
||||
|
||||
def input_product(self, prompt=None, end_prompt=None):
|
||||
product = None
|
||||
|
@ -239,47 +268,73 @@ class Menu(object):
|
|||
return product
|
||||
|
||||
def retrieve_product(self, search_str):
|
||||
return self.search_ui(search_product, search_str, 'product')
|
||||
return self.search_ui(search_product, search_str, "product")
|
||||
|
||||
def input_thing(self, prompt=None, end_prompt=None, permitted_things=('user', 'product'),
|
||||
add_nonexisting=(), empty_input_permitted=False, find_hidden_products=True):
|
||||
def input_thing(
|
||||
self,
|
||||
prompt=None,
|
||||
end_prompt=None,
|
||||
permitted_things=("user", "product"),
|
||||
add_nonexisting=(),
|
||||
empty_input_permitted=False,
|
||||
find_hidden_products=True,
|
||||
):
|
||||
result = None
|
||||
while result is None:
|
||||
search_str = self.input_str(prompt, end_prompt)
|
||||
if search_str == '' and empty_input_permitted:
|
||||
if search_str == "" and empty_input_permitted:
|
||||
return None
|
||||
result = self.search_for_thing(search_str, permitted_things, add_nonexisting, find_hidden_products)
|
||||
result = self.search_for_thing(
|
||||
search_str, permitted_things, add_nonexisting, find_hidden_products
|
||||
)
|
||||
return result
|
||||
|
||||
def input_multiple(self, prompt=None, end_prompt=None, permitted_things=('user', 'product'),
|
||||
add_nonexisting=(), empty_input_permitted=False, find_hidden_products=True):
|
||||
def input_multiple(
|
||||
self,
|
||||
prompt=None,
|
||||
end_prompt=None,
|
||||
permitted_things=("user", "product"),
|
||||
add_nonexisting=(),
|
||||
empty_input_permitted=False,
|
||||
find_hidden_products=True,
|
||||
):
|
||||
result = None
|
||||
num = 0
|
||||
while result is None:
|
||||
search_str = self.input_str(prompt, end_prompt)
|
||||
search_lst = search_str.split(" ")
|
||||
if search_str == '' and empty_input_permitted:
|
||||
if search_str == "" and empty_input_permitted:
|
||||
return None
|
||||
else:
|
||||
result = self.search_for_thing(search_str, permitted_things, add_nonexisting, find_hidden_products)
|
||||
result = self.search_for_thing(
|
||||
search_str, permitted_things, add_nonexisting, find_hidden_products
|
||||
)
|
||||
num = 1
|
||||
|
||||
if (result is None) and (len(search_lst) > 1):
|
||||
print('Interpreting input as "<number> <product>"')
|
||||
try:
|
||||
num = int(search_lst[0])
|
||||
result = self.search_for_thing(" ".join(search_lst[1:]), permitted_things, add_nonexisting,
|
||||
find_hidden_products)
|
||||
result = self.search_for_thing(
|
||||
" ".join(search_lst[1:]),
|
||||
permitted_things,
|
||||
add_nonexisting,
|
||||
find_hidden_products,
|
||||
)
|
||||
# Her kan det legges inn en except ValueError,
|
||||
# men da blir det fort mye plaging av brukeren
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return result, num
|
||||
|
||||
def search_for_thing(self, search_str, permitted_things=('user', 'product'),
|
||||
add_non_existing=(), find_hidden_products=True):
|
||||
search_fun = {'user': search_user,
|
||||
'product': search_product}
|
||||
def search_for_thing(
|
||||
self,
|
||||
search_str,
|
||||
permitted_things=("user", "product"),
|
||||
add_non_existing=(),
|
||||
find_hidden_products=True,
|
||||
):
|
||||
search_fun = {"user": search_user, "product": search_product}
|
||||
results = {}
|
||||
result_values = {}
|
||||
for thing in permitted_things:
|
||||
|
@ -287,8 +342,12 @@ class Menu(object):
|
|||
result_values[thing] = self.search_result_value(results[thing])
|
||||
selected_thing = argmax(result_values)
|
||||
if not results[selected_thing]:
|
||||
thing_for_type = {'card': 'user', 'username': 'user',
|
||||
'bar_code': 'product', 'rfid': 'rfid'}
|
||||
thing_for_type = {
|
||||
"card": "user",
|
||||
"username": "user",
|
||||
"bar_code": "product",
|
||||
"rfid": "rfid",
|
||||
}
|
||||
type_guess = guess_data_type(search_str)
|
||||
if type_guess is not None and thing_for_type[type_guess] in add_non_existing:
|
||||
return self.search_add(search_str)
|
||||
|
@ -310,32 +369,39 @@ class Menu(object):
|
|||
|
||||
def search_add(self, string):
|
||||
type_guess = guess_data_type(string)
|
||||
if type_guess == 'username':
|
||||
if type_guess == "username":
|
||||
print(f'"{string}" looks like a username, but no such user exists.')
|
||||
if self.confirm(f'Create user {string}?'):
|
||||
if self.confirm(f"Create user {string}?"):
|
||||
user = User(string, None)
|
||||
self.session.add(user)
|
||||
return user
|
||||
return None
|
||||
if type_guess == 'card':
|
||||
selector = Selector(f'"{string}" looks like a card number, but no user with that card number exists.',
|
||||
[('create', f'Create user with card number {string}'),
|
||||
('set', f'Set card number of an existing user to {string}')])
|
||||
if type_guess == "card":
|
||||
selector = Selector(
|
||||
f'"{string}" looks like a card number, but no user with that card number exists.',
|
||||
[
|
||||
("create", f"Create user with card number {string}"),
|
||||
("set", f"Set card number of an existing user to {string}"),
|
||||
],
|
||||
)
|
||||
selection = selector.execute()
|
||||
if selection == 'create':
|
||||
username = self.input_str('Username for new user (should be same as PVV username)',
|
||||
User.name_re, (1, 10))
|
||||
if selection == "create":
|
||||
username = self.input_str(
|
||||
"Username for new user (should be same as PVV username)",
|
||||
User.name_re,
|
||||
(1, 10),
|
||||
)
|
||||
user = User(username, string)
|
||||
self.session.add(user)
|
||||
return user
|
||||
if selection == 'set':
|
||||
user = self.input_user('User to set card number for')
|
||||
if selection == "set":
|
||||
user = self.input_user("User to set card number for")
|
||||
old_card = user.card
|
||||
user.card = string
|
||||
print(f'Card number of {user.name} set to {string} (was {old_card})')
|
||||
print(f"Card number of {user.name} set to {string} (was {old_card})")
|
||||
return user
|
||||
return None
|
||||
if type_guess == 'bar_code':
|
||||
if type_guess == "bar_code":
|
||||
print(f'"{string}" looks like the bar code for a product, but no such product exists.')
|
||||
return None
|
||||
|
||||
|
@ -356,13 +422,14 @@ class Menu(object):
|
|||
return None
|
||||
limit = 9
|
||||
if len(result) > limit:
|
||||
select_header = f'{len(result):d} {thing}s matching "{search_str}"; showing first {limit:d}'
|
||||
select_header = (
|
||||
f'{len(result):d} {thing}s matching "{search_str}"; showing first {limit:d}'
|
||||
)
|
||||
select_items = result[:limit]
|
||||
else:
|
||||
select_header = f'{len(result):d} {thing}s matching "{search_str}"'
|
||||
select_items = result
|
||||
selector = Selector(select_header, items=select_items,
|
||||
return_index=False)
|
||||
selector = Selector(select_header, items=select_items, return_index=False)
|
||||
return selector.execute()
|
||||
|
||||
@staticmethod
|
||||
|
@ -377,11 +444,12 @@ class Menu(object):
|
|||
print(self.header())
|
||||
|
||||
def pause(self):
|
||||
self.input_str('.', end_prompt="")
|
||||
self.input_str(".", end_prompt="")
|
||||
|
||||
@staticmethod
|
||||
def general_help():
|
||||
print('''
|
||||
print(
|
||||
"""
|
||||
DIBBLER HELP
|
||||
|
||||
The following commands are recognized (almost) everywhere:
|
||||
|
@ -402,14 +470,15 @@ class Menu(object):
|
|||
of money PVVVV owes the user. This value decreases with the
|
||||
appropriate amount when you register a purchase, and you may increase
|
||||
it by putting money in the box and using the "Adjust credit" menu.
|
||||
''')
|
||||
"""
|
||||
)
|
||||
|
||||
def local_help(self):
|
||||
if self.help_text is None:
|
||||
print('no help here')
|
||||
print("no help here")
|
||||
else:
|
||||
print('')
|
||||
print(f'Help for {self.header()}:')
|
||||
print("")
|
||||
print(f"Help for {self.header()}:")
|
||||
print(self.help_text)
|
||||
|
||||
def execute(self, **kwargs):
|
||||
|
@ -431,7 +500,7 @@ class Menu(object):
|
|||
self.print_header()
|
||||
self.set_context(None)
|
||||
if len(self.items) == 0:
|
||||
self.printc('(empty menu)')
|
||||
self.printc("(empty menu)")
|
||||
self.pause()
|
||||
return None
|
||||
for i in range(len(self.items)):
|
||||
|
@ -452,37 +521,52 @@ class MessageMenu(Menu):
|
|||
|
||||
def _execute(self):
|
||||
self.print_header()
|
||||
print('')
|
||||
print("")
|
||||
print(self.message)
|
||||
if self.pause_after_message:
|
||||
self.pause()
|
||||
|
||||
|
||||
class ConfirmMenu(Menu):
|
||||
def __init__(self, prompt='confirm? ', end_prompt=": ", default=None, timeout=0):
|
||||
Menu.__init__(self, 'question', prompt=prompt, end_prompt=end_prompt,
|
||||
exit_disallowed_msg='Please answer yes or no')
|
||||
def __init__(self, prompt="confirm? ", end_prompt=": ", default=None, timeout=0):
|
||||
Menu.__init__(
|
||||
self,
|
||||
"question",
|
||||
prompt=prompt,
|
||||
end_prompt=end_prompt,
|
||||
exit_disallowed_msg="Please answer yes or no",
|
||||
)
|
||||
self.default = default
|
||||
self.timeout = timeout
|
||||
|
||||
def _execute(self):
|
||||
options = {True: '[y]/n', False: 'y/[n]', None: 'y/n'}[self.default]
|
||||
options = {True: "[y]/n", False: "y/[n]", None: "y/n"}[self.default]
|
||||
while True:
|
||||
result = self.input_str(f'{self.prompt} ({options})', end_prompt=": ", timeout=self.timeout)
|
||||
result = self.input_str(
|
||||
f"{self.prompt} ({options})", end_prompt=": ", timeout=self.timeout
|
||||
)
|
||||
result = result.lower().strip()
|
||||
if result in ['y', 'yes']:
|
||||
if result in ["y", "yes"]:
|
||||
return True
|
||||
elif result in ['n', 'no']:
|
||||
elif result in ["n", "no"]:
|
||||
return False
|
||||
elif self.default is not None and result == '':
|
||||
elif self.default is not None and result == "":
|
||||
return self.default
|
||||
else:
|
||||
print('Please answer yes or no')
|
||||
print("Please answer yes or no")
|
||||
|
||||
|
||||
class Selector(Menu):
|
||||
def __init__(self, name, items=None, prompt='select', return_index=True, exit_msg=None, exit_confirm_msg=None,
|
||||
help_text=None):
|
||||
def __init__(
|
||||
self,
|
||||
name,
|
||||
items=None,
|
||||
prompt="select",
|
||||
return_index=True,
|
||||
exit_msg=None,
|
||||
exit_confirm_msg=None,
|
||||
help_text=None,
|
||||
):
|
||||
if items is None:
|
||||
items = []
|
||||
Menu.__init__(self, name, items, prompt, return_index=return_index, exit_msg=exit_msg)
|
||||
|
@ -495,9 +579,9 @@ class Selector(Menu):
|
|||
|
||||
def local_help(self):
|
||||
if self.help_text is None:
|
||||
print('This is a selection menu. Enter one of the listed numbers, or')
|
||||
print('\'exit\' to go out and do something else.')
|
||||
print("This is a selection menu. Enter one of the listed numbers, or")
|
||||
print("'exit' to go out and do something else.")
|
||||
else:
|
||||
print('')
|
||||
print(f'Help for selector ({self.name}):')
|
||||
print("")
|
||||
print(f"Help for selector ({self.name}):")
|
||||
print(self.help_text)
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
import random
|
||||
|
@ -10,8 +9,9 @@ from .buymenu import BuyMenu
|
|||
from .faq import FAQMenu
|
||||
from .helpermenus import Menu
|
||||
|
||||
faq_commands = ['faq']
|
||||
restart_commands = ['restart']
|
||||
faq_commands = ["faq"]
|
||||
restart_commands = ["restart"]
|
||||
|
||||
|
||||
def restart():
|
||||
# Does not work if the script is not executable, or if it was
|
||||
|
@ -41,18 +41,24 @@ class MainMenu(Menu):
|
|||
FAQMenu().execute()
|
||||
return True
|
||||
if result in restart_commands:
|
||||
if self.confirm('Restart Dibbler?'):
|
||||
if self.confirm("Restart Dibbler?"):
|
||||
restart()
|
||||
pass
|
||||
return True
|
||||
elif result == 'c':
|
||||
os.system('echo -e "\033[' + str(random.randint(40, 49)) + ';' + str(random.randint(30, 37)) + ';5m"')
|
||||
os.system('clear')
|
||||
elif result == "c":
|
||||
os.system(
|
||||
'echo -e "\033['
|
||||
+ str(random.randint(40, 49))
|
||||
+ ";"
|
||||
+ str(random.randint(30, 37))
|
||||
+ ';5m"'
|
||||
)
|
||||
os.system("clear")
|
||||
self.show_context()
|
||||
return True
|
||||
elif result == 'cs':
|
||||
elif result == "cs":
|
||||
os.system('echo -e "\033[0m"')
|
||||
os.system('clear')
|
||||
os.system("clear")
|
||||
self.show_context()
|
||||
return True
|
||||
return False
|
||||
|
|
|
@ -9,24 +9,21 @@ from .helpermenus import Menu, Selector
|
|||
|
||||
class TransferMenu(Menu):
|
||||
def __init__(self):
|
||||
Menu.__init__(self, 'Transfer credit between users',
|
||||
uses_db=True)
|
||||
Menu.__init__(self, "Transfer credit between users", uses_db=True)
|
||||
|
||||
def _execute(self):
|
||||
self.print_header()
|
||||
amount = self.input_int('Transfer amount', 1, 100000)
|
||||
self.set_context(f'Transferring {amount:d} kr', display=False)
|
||||
user1 = self.input_user('From user')
|
||||
self.add_to_context(f' from {user1.name}')
|
||||
user2 = self.input_user('To user')
|
||||
self.add_to_context(f' to {user2.name}')
|
||||
comment = self.input_str('Comment')
|
||||
self.add_to_context(f' (comment) {user2.name}')
|
||||
amount = self.input_int("Transfer amount", 1, 100000)
|
||||
self.set_context(f"Transferring {amount:d} kr", display=False)
|
||||
user1 = self.input_user("From user")
|
||||
self.add_to_context(f" from {user1.name}")
|
||||
user2 = self.input_user("To user")
|
||||
self.add_to_context(f" to {user2.name}")
|
||||
comment = self.input_str("Comment")
|
||||
self.add_to_context(f" (comment) {user2.name}")
|
||||
|
||||
t1 = Transaction(user1, amount,
|
||||
f'transfer to {user2.name} "{comment}"')
|
||||
t2 = Transaction(user2, -amount,
|
||||
f'transfer from {user1.name} "{comment}"')
|
||||
t1 = Transaction(user1, amount, f'transfer to {user2.name} "{comment}"')
|
||||
t2 = Transaction(user2, -amount, f'transfer from {user1.name} "{comment}"')
|
||||
t1.perform_transaction()
|
||||
t2.perform_transaction()
|
||||
self.session.add(t1)
|
||||
|
@ -38,35 +35,43 @@ class TransferMenu(Menu):
|
|||
print(f"User {user2}'s credit is now {user2.credit:d} kr")
|
||||
print(f"Comment: {comment}")
|
||||
except sqlalchemy.exc.SQLAlchemyError as e:
|
||||
print(f'Could not perform transfer: {e}')
|
||||
print(f"Could not perform transfer: {e}")
|
||||
# self.pause()
|
||||
|
||||
|
||||
class ShowUserMenu(Menu):
|
||||
def __init__(self):
|
||||
Menu.__init__(self, 'Show user', uses_db=True)
|
||||
Menu.__init__(self, "Show user", uses_db=True)
|
||||
|
||||
def _execute(self):
|
||||
self.print_header()
|
||||
user = self.input_user('User name, card number or RFID')
|
||||
print(f'User name: {user.name}')
|
||||
print(f'Card number: {user.card}')
|
||||
print(f'RFID: {user.rfid}')
|
||||
print(f'Credit: {user.credit} kr')
|
||||
selector = Selector(f'What do you want to know about {user.name}?',
|
||||
items=[('transactions', 'Recent transactions (List of last ' + str(
|
||||
config.getint('limits', 'user_recent_transaction_limit')) + ')'),
|
||||
('products', f'Which products {user.name} has bought, and how many'),
|
||||
('transactions-all', 'Everything (List of all transactions)')])
|
||||
user = self.input_user("User name, card number or RFID")
|
||||
print(f"User name: {user.name}")
|
||||
print(f"Card number: {user.card}")
|
||||
print(f"RFID: {user.rfid}")
|
||||
print(f"Credit: {user.credit} kr")
|
||||
selector = Selector(
|
||||
f"What do you want to know about {user.name}?",
|
||||
items=[
|
||||
(
|
||||
"transactions",
|
||||
"Recent transactions (List of last "
|
||||
+ str(config.getint("limits", "user_recent_transaction_limit"))
|
||||
+ ")",
|
||||
),
|
||||
("products", f"Which products {user.name} has bought, and how many"),
|
||||
("transactions-all", "Everything (List of all transactions)"),
|
||||
],
|
||||
)
|
||||
what = selector.execute()
|
||||
if what == 'transactions':
|
||||
self.print_transactions(user, config.getint('limits', 'user_recent_transaction_limit'))
|
||||
elif what == 'products':
|
||||
if what == "transactions":
|
||||
self.print_transactions(user, config.getint("limits", "user_recent_transaction_limit"))
|
||||
elif what == "products":
|
||||
self.print_purchased_products(user)
|
||||
elif what == 'transactions-all':
|
||||
elif what == "transactions-all":
|
||||
self.print_transactions(user)
|
||||
else:
|
||||
print('What what?')
|
||||
print("What what?")
|
||||
|
||||
@staticmethod
|
||||
def print_transactions(user, limit=None):
|
||||
|
@ -77,7 +82,7 @@ class ShowUserMenu(Menu):
|
|||
string = f"{user.name}'s transactions ({num_trans:d}):\n"
|
||||
else:
|
||||
string = f"{user.name}'s transactions ({num_trans:d}, showing only last {limit:d}):\n"
|
||||
for t in user.transactions[-1:-limit - 1:-1]:
|
||||
for t in user.transactions[-1 : -limit - 1 : -1]:
|
||||
string += f" * {t.time.isoformat(' ')}: {'in' if t.amount < 0 else 'out'} {abs(t.amount)} kr, "
|
||||
if t.purchase:
|
||||
products = []
|
||||
|
@ -88,14 +93,14 @@ class ShowUserMenu(Menu):
|
|||
amount = ""
|
||||
product = f"{amount}{entry.product.name}"
|
||||
products.append(product)
|
||||
string += 'purchase ('
|
||||
string += ', '.join(products)
|
||||
string += ')'
|
||||
string += "purchase ("
|
||||
string += ", ".join(products)
|
||||
string += ")"
|
||||
if t.penalty > 1:
|
||||
string += f' * {t.penalty:d}x penalty applied'
|
||||
string += f" * {t.penalty:d}x penalty applied"
|
||||
else:
|
||||
string += t.description
|
||||
string += '\n'
|
||||
string += "\n"
|
||||
less(string)
|
||||
|
||||
@staticmethod
|
||||
|
@ -108,54 +113,55 @@ class ShowUserMenu(Menu):
|
|||
products.append((product, count))
|
||||
num_products = len(products)
|
||||
if num_products == 0:
|
||||
print('No products purchased yet')
|
||||
print("No products purchased yet")
|
||||
else:
|
||||
text = ''
|
||||
text += 'Products purchased:\n'
|
||||
text = ""
|
||||
text += "Products purchased:\n"
|
||||
for product, count in products:
|
||||
text += f'{product.name:<47} {count:>3}\n'
|
||||
text += f"{product.name:<47} {count:>3}\n"
|
||||
less(text)
|
||||
|
||||
|
||||
class UserListMenu(Menu):
|
||||
def __init__(self):
|
||||
Menu.__init__(self, 'User list', uses_db=True)
|
||||
Menu.__init__(self, "User list", uses_db=True)
|
||||
|
||||
def _execute(self):
|
||||
self.print_header()
|
||||
user_list = self.session.query(User).all()
|
||||
total_credit = self.session.query(sqlalchemy.func.sum(User.credit)).first()[0]
|
||||
|
||||
line_format = '%-12s | %6s\n'
|
||||
hline = '---------------------\n'
|
||||
text = ''
|
||||
text += line_format % ('username', 'credit')
|
||||
line_format = "%-12s | %6s\n"
|
||||
hline = "---------------------\n"
|
||||
text = ""
|
||||
text += line_format % ("username", "credit")
|
||||
text += hline
|
||||
for user in user_list:
|
||||
text += line_format % (user.name, user.credit)
|
||||
text += hline
|
||||
text += line_format % ('total credit', total_credit)
|
||||
text += line_format % ("total credit", total_credit)
|
||||
less(text)
|
||||
|
||||
|
||||
class AdjustCreditMenu(Menu):
|
||||
def __init__(self):
|
||||
Menu.__init__(self, 'Adjust credit', uses_db=True)
|
||||
Menu.__init__(self, "Adjust credit", uses_db=True)
|
||||
|
||||
def _execute(self):
|
||||
self.print_header()
|
||||
user = self.input_user('User')
|
||||
user = self.input_user("User")
|
||||
print(f"User {user.name}'s credit is {user.credit:d} kr")
|
||||
self.set_context(f'Adjusting credit for user {user.name}', display=False)
|
||||
print('(Note on sign convention: Enter a positive amount here if you have')
|
||||
print('added money to the PVVVV money box, a negative amount if you have')
|
||||
print('taken money from it)')
|
||||
amount = self.input_int('Add amount', -100000, 100000)
|
||||
self.set_context(f"Adjusting credit for user {user.name}", display=False)
|
||||
print("(Note on sign convention: Enter a positive amount here if you have")
|
||||
print("added money to the PVVVV money box, a negative amount if you have")
|
||||
print("taken money from it)")
|
||||
amount = self.input_int("Add amount", -100000, 100000)
|
||||
print('(The "log message" will show up in the transaction history in the')
|
||||
print('"Show user" menu. It is not necessary to enter a message, but it')
|
||||
print('might be useful to help you remember why you adjusted the credit)')
|
||||
description = self.input_str('Log message', length_range=(0, 50))
|
||||
if description == '':
|
||||
description = 'manually adjusted credit'
|
||||
print("might be useful to help you remember why you adjusted the credit)")
|
||||
description = self.input_str("Log message", length_range=(0, 50))
|
||||
if description == "":
|
||||
description = "manually adjusted credit"
|
||||
transaction = Transaction(user, -amount, description)
|
||||
transaction.perform_transaction()
|
||||
self.session.add(transaction)
|
||||
|
@ -163,40 +169,56 @@ class AdjustCreditMenu(Menu):
|
|||
self.session.commit()
|
||||
print(f"User {user.name}'s credit is now {user.credit:d} kr")
|
||||
except sqlalchemy.exc.SQLAlchemyError as e:
|
||||
print(f'Could not store transaction: {e}')
|
||||
print(f"Could not store transaction: {e}")
|
||||
# self.pause()
|
||||
|
||||
|
||||
class ProductListMenu(Menu):
|
||||
def __init__(self):
|
||||
Menu.__init__(self, 'Product list', uses_db=True)
|
||||
Menu.__init__(self, "Product list", uses_db=True)
|
||||
|
||||
def _execute(self):
|
||||
self.print_header()
|
||||
text = ''
|
||||
product_list = self.session.query(Product).filter(Product.hidden.is_(False)).order_by(Product.stock.desc())
|
||||
text = ""
|
||||
product_list = (
|
||||
self.session.query(Product)
|
||||
.filter(Product.hidden.is_(False))
|
||||
.order_by(Product.stock.desc())
|
||||
)
|
||||
total_value = 0
|
||||
for p in product_list:
|
||||
total_value += p.price * p.stock
|
||||
line_format = '%-15s | %5s | %-' + str(Product.name_length) + 's | %5s \n'
|
||||
text += line_format % ('bar code', 'price', 'name', 'stock')
|
||||
text += 78 * '-' + '\n'
|
||||
line_format = "%-15s | %5s | %-" + str(Product.name_length) + "s | %5s \n"
|
||||
text += line_format % ("bar code", "price", "name", "stock")
|
||||
text += 78 * "-" + "\n"
|
||||
for p in product_list:
|
||||
text += line_format % (p.bar_code, p.price, p.name, p.stock)
|
||||
text += 78 * '-' + '\n'
|
||||
text += line_format % ('Total value', total_value, '', '',)
|
||||
text += 78 * "-" + "\n"
|
||||
text += line_format % (
|
||||
"Total value",
|
||||
total_value,
|
||||
"",
|
||||
"",
|
||||
)
|
||||
less(text)
|
||||
|
||||
|
||||
class ProductSearchMenu(Menu):
|
||||
def __init__(self):
|
||||
Menu.__init__(self, 'Product search', uses_db=True)
|
||||
Menu.__init__(self, "Product search", uses_db=True)
|
||||
|
||||
def _execute(self):
|
||||
self.print_header()
|
||||
self.set_context('Enter (part of) product name or bar code')
|
||||
self.set_context("Enter (part of) product name or bar code")
|
||||
product = self.input_product()
|
||||
print('Result: %s, price: %d kr, bar code: %s, stock: %d, hidden: %s' % (product.name, product.price,
|
||||
product.bar_code, product.stock,
|
||||
("Y" if product.hidden else "N")))
|
||||
print(
|
||||
"Result: %s, price: %d kr, bar code: %s, stock: %d, hidden: %s"
|
||||
% (
|
||||
product.name,
|
||||
product.price,
|
||||
product.bar_code,
|
||||
product.stock,
|
||||
("Y" if product.hidden else "N"),
|
||||
)
|
||||
)
|
||||
# self.pause()
|
||||
|
|
|
@ -9,17 +9,17 @@ from .helpermenus import Menu
|
|||
|
||||
class PrintLabelMenu(Menu):
|
||||
def __init__(self):
|
||||
Menu.__init__(self, 'Print a label', uses_db=True)
|
||||
self.help_text = '''
|
||||
Menu.__init__(self, "Print a label", uses_db=True)
|
||||
self.help_text = """
|
||||
Prints out a product bar code on the printer
|
||||
|
||||
Put it up somewhere in the vicinity.
|
||||
'''
|
||||
"""
|
||||
|
||||
def _execute(self):
|
||||
self.print_header()
|
||||
|
||||
thing = self.input_thing('Product/User')
|
||||
thing = self.input_thing("Product/User")
|
||||
|
||||
if isinstance(thing, Product):
|
||||
if re.match(r"^[0-9]{13}$", thing.bar_code):
|
||||
|
@ -32,14 +32,14 @@ Put it up somewhere in the vicinity.
|
|||
thing.bar_code,
|
||||
thing.name,
|
||||
barcode_type=bar_type,
|
||||
rotate=config.getboolean('printer', 'rotate'),
|
||||
rotate=config.getboolean("printer", "rotate"),
|
||||
printer_type="QL-700",
|
||||
label_type=config.get('printer', 'label_type'),
|
||||
label_type=config.get("printer", "label_type"),
|
||||
)
|
||||
elif isinstance(thing, User):
|
||||
print_name_label(
|
||||
text=thing.name,
|
||||
label_type=config.get('printer', 'label_type'),
|
||||
rotate=config.getboolean('printer', 'rotate'),
|
||||
printer_type="QL-700"
|
||||
label_type=config.get("printer", "label_type"),
|
||||
rotate=config.getboolean("printer", "rotate"),
|
||||
printer_type="QL-700",
|
||||
)
|
||||
|
|
|
@ -6,30 +6,40 @@ from dibbler.lib.statistikkHelpers import statisticsTextOnly
|
|||
|
||||
from .helpermenus import Menu
|
||||
|
||||
__all__ = ["ProductPopularityMenu", "ProductRevenueMenu", "BalanceMenu", "LoggedStatisticsMenu"]
|
||||
__all__ = [
|
||||
"ProductPopularityMenu",
|
||||
"ProductRevenueMenu",
|
||||
"BalanceMenu",
|
||||
"LoggedStatisticsMenu",
|
||||
]
|
||||
|
||||
|
||||
class ProductPopularityMenu(Menu):
|
||||
def __init__(self):
|
||||
Menu.__init__(self, 'Products by popularity', uses_db=True)
|
||||
Menu.__init__(self, "Products by popularity", uses_db=True)
|
||||
|
||||
def _execute(self):
|
||||
self.print_header()
|
||||
text = ''
|
||||
sub = \
|
||||
self.session.query(PurchaseEntry.product_id,
|
||||
func.sum(PurchaseEntry.amount).label('purchase_count')) \
|
||||
.filter(PurchaseEntry.amount > 0).group_by(PurchaseEntry.product_id) \
|
||||
.subquery()
|
||||
product_list = \
|
||||
self.session.query(Product, sub.c.purchase_count) \
|
||||
.outerjoin((sub, Product.product_id == sub.c.product_id)) \
|
||||
.order_by(desc(sub.c.purchase_count)) \
|
||||
.filter(sub.c.purchase_count is not None) \
|
||||
.all()
|
||||
line_format = '{0:10s} | {1:>45s}\n'
|
||||
text += line_format.format('items sold', 'product')
|
||||
text += '-' * (31 + Product.name_length) + '\n'
|
||||
text = ""
|
||||
sub = (
|
||||
self.session.query(
|
||||
PurchaseEntry.product_id,
|
||||
func.sum(PurchaseEntry.amount).label("purchase_count"),
|
||||
)
|
||||
.filter(PurchaseEntry.amount > 0)
|
||||
.group_by(PurchaseEntry.product_id)
|
||||
.subquery()
|
||||
)
|
||||
product_list = (
|
||||
self.session.query(Product, sub.c.purchase_count)
|
||||
.outerjoin((sub, Product.product_id == sub.c.product_id))
|
||||
.order_by(desc(sub.c.purchase_count))
|
||||
.filter(sub.c.purchase_count is not None)
|
||||
.all()
|
||||
)
|
||||
line_format = "{0:10s} | {1:>45s}\n"
|
||||
text += line_format.format("items sold", "product")
|
||||
text += "-" * (31 + Product.name_length) + "\n"
|
||||
for product, number in product_list:
|
||||
if number is None:
|
||||
continue
|
||||
|
@ -39,64 +49,78 @@ class ProductPopularityMenu(Menu):
|
|||
|
||||
class ProductRevenueMenu(Menu):
|
||||
def __init__(self):
|
||||
Menu.__init__(self, 'Products by revenue', uses_db=True)
|
||||
Menu.__init__(self, "Products by revenue", uses_db=True)
|
||||
|
||||
def _execute(self):
|
||||
self.print_header()
|
||||
text = ''
|
||||
sub = \
|
||||
self.session.query(PurchaseEntry.product_id,
|
||||
func.sum(PurchaseEntry.amount).label('purchase_count')) \
|
||||
.filter(PurchaseEntry.amount > 0).group_by(PurchaseEntry.product_id) \
|
||||
.subquery()
|
||||
product_list = \
|
||||
self.session.query(Product, sub.c.purchase_count) \
|
||||
.outerjoin((sub, Product.product_id == sub.c.product_id)) \
|
||||
.order_by(desc(sub.c.purchase_count * Product.price)) \
|
||||
.filter(sub.c.purchase_count is not None) \
|
||||
.all()
|
||||
line_format = '{0:7s} | {1:10s} | {2:6s} | {3:>45s}\n'
|
||||
text += line_format.format('revenue', 'items sold', 'price', 'product')
|
||||
text += '-' * (31 + Product.name_length) + '\n'
|
||||
text = ""
|
||||
sub = (
|
||||
self.session.query(
|
||||
PurchaseEntry.product_id,
|
||||
func.sum(PurchaseEntry.amount).label("purchase_count"),
|
||||
)
|
||||
.filter(PurchaseEntry.amount > 0)
|
||||
.group_by(PurchaseEntry.product_id)
|
||||
.subquery()
|
||||
)
|
||||
product_list = (
|
||||
self.session.query(Product, sub.c.purchase_count)
|
||||
.outerjoin((sub, Product.product_id == sub.c.product_id))
|
||||
.order_by(desc(sub.c.purchase_count * Product.price))
|
||||
.filter(sub.c.purchase_count is not None)
|
||||
.all()
|
||||
)
|
||||
line_format = "{0:7s} | {1:10s} | {2:6s} | {3:>45s}\n"
|
||||
text += line_format.format("revenue", "items sold", "price", "product")
|
||||
text += "-" * (31 + Product.name_length) + "\n"
|
||||
for product, number in product_list:
|
||||
if number is None:
|
||||
continue
|
||||
text += line_format.format(str(number * product.price), str(number), str(product.price), product.name)
|
||||
text += line_format.format(
|
||||
str(number * product.price),
|
||||
str(number),
|
||||
str(product.price),
|
||||
product.name,
|
||||
)
|
||||
less(text)
|
||||
|
||||
|
||||
class BalanceMenu(Menu):
|
||||
def __init__(self):
|
||||
Menu.__init__(self, 'Total balance of PVVVV', uses_db=True)
|
||||
Menu.__init__(self, "Total balance of PVVVV", uses_db=True)
|
||||
|
||||
def _execute(self):
|
||||
self.print_header()
|
||||
text = ''
|
||||
text = ""
|
||||
total_value = 0
|
||||
product_list = self.session.query(Product).filter(Product.stock > 0).all()
|
||||
for p in product_list:
|
||||
total_value += p.stock * p.price
|
||||
|
||||
total_positive_credit = self.session.query(func.sum(User.credit)).filter(User.credit > 0).first()[0]
|
||||
total_negative_credit = self.session.query(func.sum(User.credit)).filter(User.credit < 0).first()[0]
|
||||
total_positive_credit = (
|
||||
self.session.query(func.sum(User.credit)).filter(User.credit > 0).first()[0]
|
||||
)
|
||||
total_negative_credit = (
|
||||
self.session.query(func.sum(User.credit)).filter(User.credit < 0).first()[0]
|
||||
)
|
||||
|
||||
total_credit = total_positive_credit + total_negative_credit
|
||||
total_balance = total_value - total_credit
|
||||
|
||||
line_format = '%15s | %5d \n'
|
||||
text += line_format % ('Total value', total_value)
|
||||
text += 24 * '-' + '\n'
|
||||
text += line_format % ('Positive credit', total_positive_credit)
|
||||
text += line_format % ('Negative credit', total_negative_credit)
|
||||
text += line_format % ('Total credit', total_credit)
|
||||
text += 24 * '-' + '\n'
|
||||
text += line_format % ('Total balance', total_balance)
|
||||
line_format = "%15s | %5d \n"
|
||||
text += line_format % ("Total value", total_value)
|
||||
text += 24 * "-" + "\n"
|
||||
text += line_format % ("Positive credit", total_positive_credit)
|
||||
text += line_format % ("Negative credit", total_negative_credit)
|
||||
text += line_format % ("Total credit", total_credit)
|
||||
text += 24 * "-" + "\n"
|
||||
text += line_format % ("Total balance", total_balance)
|
||||
less(text)
|
||||
|
||||
|
||||
class LoggedStatisticsMenu(Menu):
|
||||
def __init__(self):
|
||||
Menu.__init__(self, 'Statistics from log', uses_db=True)
|
||||
Menu.__init__(self, "Statistics from log", uses_db=True)
|
||||
|
||||
def _execute(self):
|
||||
statisticsTextOnly()
|
||||
|
|
|
@ -9,6 +9,7 @@ from sqlalchemy.orm.collections import (
|
|||
InstrumentedSet,
|
||||
)
|
||||
|
||||
|
||||
class Base(DeclarativeBase):
|
||||
metadata = MetaData(
|
||||
naming_convention={
|
||||
|
@ -16,7 +17,7 @@ class Base(DeclarativeBase):
|
|||
"uq": "uq_%(table_name)s_%(column_0_name)s",
|
||||
"ck": "ck_%(table_name)s_`%(constraint_name)s`",
|
||||
"fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
|
||||
"pk": "pk_%(table_name)s"
|
||||
"pk": "pk_%(table_name)s",
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -26,15 +27,18 @@ class Base(DeclarativeBase):
|
|||
|
||||
def __repr__(self) -> str:
|
||||
columns = ", ".join(
|
||||
f"{k}={repr(v)}" for k, v in self.__dict__.items() if not any([
|
||||
k.startswith("_"),
|
||||
|
||||
# Ensure that we don't try to print out the entire list of
|
||||
# relationships, which could create an infinite loop
|
||||
isinstance(v, Base),
|
||||
isinstance(v, InstrumentedList),
|
||||
isinstance(v, InstrumentedSet),
|
||||
isinstance(v, InstrumentedDict),
|
||||
])
|
||||
f"{k}={repr(v)}"
|
||||
for k, v in self.__dict__.items()
|
||||
if not any(
|
||||
[
|
||||
k.startswith("_"),
|
||||
# Ensure that we don't try to print out the entire list of
|
||||
# relationships, which could create an infinite loop
|
||||
isinstance(v, Base),
|
||||
isinstance(v, InstrumentedList),
|
||||
isinstance(v, InstrumentedSet),
|
||||
isinstance(v, InstrumentedDict),
|
||||
]
|
||||
)
|
||||
)
|
||||
return f"<{self.__class__.__name__}({columns})>"
|
||||
|
|
|
@ -13,12 +13,14 @@ from sqlalchemy.orm import (
|
|||
)
|
||||
|
||||
from .Base import Base
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .PurchaseEntry import PurchaseEntry
|
||||
from .UserProducts import UserProducts
|
||||
|
||||
|
||||
class Product(Base):
|
||||
__tablename__ = 'products'
|
||||
__tablename__ = "products"
|
||||
|
||||
product_id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
||||
bar_code: Mapped[str] = mapped_column(String(13))
|
||||
|
@ -34,7 +36,7 @@ class Product(Base):
|
|||
name_re = r".+"
|
||||
name_length = 45
|
||||
|
||||
def __init__(self, bar_code, name, price, stock=0, hidden = False):
|
||||
def __init__(self, bar_code, name, price, stock=0, hidden=False):
|
||||
self.name = name
|
||||
self.bar_code = bar_code
|
||||
self.price = price
|
||||
|
|
|
@ -22,14 +22,17 @@ from .Transaction import Transaction
|
|||
if TYPE_CHECKING:
|
||||
from .PurchaseEntry import PurchaseEntry
|
||||
|
||||
|
||||
class Purchase(Base):
|
||||
__tablename__ = 'purchases'
|
||||
__tablename__ = "purchases"
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
||||
time: Mapped[datetime] = mapped_column(DateTime)
|
||||
price: Mapped[int] = mapped_column(Integer)
|
||||
|
||||
transactions: Mapped[set[Transaction]] = relationship(back_populates="purchase", order_by='Transaction.user_name')
|
||||
transactions: Mapped[set[Transaction]] = relationship(
|
||||
back_populates="purchase", order_by="Transaction.user_name"
|
||||
)
|
||||
entries: Mapped[set[PurchaseEntry]] = relationship(back_populates="purchase")
|
||||
|
||||
def __init__(self):
|
||||
|
@ -40,14 +43,14 @@ class Purchase(Base):
|
|||
|
||||
def price_per_transaction(self, round_up=True):
|
||||
if round_up:
|
||||
return int(math.ceil(float(self.price)/len(self.transactions)))
|
||||
return int(math.ceil(float(self.price) / len(self.transactions)))
|
||||
else:
|
||||
return int(math.floor(float(self.price)/len(self.transactions)))
|
||||
return int(math.floor(float(self.price) / len(self.transactions)))
|
||||
|
||||
def set_price(self, round_up=True):
|
||||
self.price = 0
|
||||
for entry in self.entries:
|
||||
self.price += entry.amount*entry.product.price
|
||||
self.price += entry.amount * entry.product.price
|
||||
if len(self.transactions) > 0:
|
||||
for t in self.transactions:
|
||||
t.amount = self.price_per_transaction(round_up=round_up)
|
||||
|
|
|
@ -17,8 +17,9 @@ if TYPE_CHECKING:
|
|||
from .Product import Product
|
||||
from .Purchase import Purchase
|
||||
|
||||
|
||||
class PurchaseEntry(Base):
|
||||
__tablename__ = 'purchase_entries'
|
||||
__tablename__ = "purchase_entries"
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
||||
amount: Mapped[int] = mapped_column(Integer)
|
||||
|
@ -26,8 +27,8 @@ class PurchaseEntry(Base):
|
|||
product_id: Mapped[int] = mapped_column(ForeignKey("products.product_id"))
|
||||
purchase_id: Mapped[int] = mapped_column(ForeignKey("purchases.id"))
|
||||
|
||||
product: Mapped[Product] = relationship(lazy='joined')
|
||||
purchase: Mapped[Purchase] = relationship(lazy='joined')
|
||||
product: Mapped[Product] = relationship(lazy="joined")
|
||||
purchase: Mapped[Purchase] = relationship(lazy="joined")
|
||||
|
||||
def __init__(self, purchase, product, amount):
|
||||
self.product = product
|
||||
|
|
|
@ -21,8 +21,9 @@ if TYPE_CHECKING:
|
|||
from .User import User
|
||||
from .Purchase import Purchase
|
||||
|
||||
|
||||
class Transaction(Base):
|
||||
__tablename__ = 'transactions'
|
||||
__tablename__ = "transactions"
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
||||
|
||||
|
@ -31,11 +32,11 @@ class Transaction(Base):
|
|||
penalty: Mapped[int] = mapped_column(Integer)
|
||||
description: Mapped[str | None] = mapped_column(String(50))
|
||||
|
||||
user_name: Mapped[str] = mapped_column(ForeignKey('users.name'))
|
||||
purchase_id: Mapped[int | None] = mapped_column(ForeignKey('purchases.id'))
|
||||
user_name: Mapped[str] = mapped_column(ForeignKey("users.name"))
|
||||
purchase_id: Mapped[int | None] = mapped_column(ForeignKey("purchases.id"))
|
||||
|
||||
user: Mapped[User] = relationship(lazy='joined')
|
||||
purchase: Mapped[Purchase] = relationship(lazy='joined')
|
||||
user: Mapped[User] = relationship(lazy="joined")
|
||||
purchase: Mapped[Purchase] = relationship(lazy="joined")
|
||||
|
||||
def __init__(self, user, amount=0, description=None, purchase=None, penalty=1):
|
||||
self.user = user
|
||||
|
|
|
@ -12,12 +12,14 @@ from sqlalchemy.orm import (
|
|||
)
|
||||
|
||||
from .Base import Base
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .UserProducts import UserProducts
|
||||
from .Transaction import Transaction
|
||||
|
||||
|
||||
class User(Base):
|
||||
__tablename__ = 'users'
|
||||
__tablename__ = "users"
|
||||
name: Mapped[str] = mapped_column(String(10), primary_key=True)
|
||||
credit: Mapped[str] = mapped_column(Integer)
|
||||
card: Mapped[str | None] = mapped_column(String(20))
|
||||
|
@ -32,10 +34,10 @@ class User(Base):
|
|||
|
||||
def __init__(self, name, card, rfid=None, credit=0):
|
||||
self.name = name
|
||||
if card == '':
|
||||
if card == "":
|
||||
card = None
|
||||
self.card = card
|
||||
if rfid == '':
|
||||
if rfid == "":
|
||||
rfid = None
|
||||
self.rfid = rfid
|
||||
self.credit = credit
|
||||
|
@ -44,4 +46,4 @@ class User(Base):
|
|||
return self.name
|
||||
|
||||
def is_anonymous(self):
|
||||
return self.card == '11122233'
|
||||
return self.card == "11122233"
|
||||
|
|
|
@ -17,10 +17,11 @@ if TYPE_CHECKING:
|
|||
from .User import User
|
||||
from .Product import Product
|
||||
|
||||
class UserProducts(Base):
|
||||
__tablename__ = 'user_products'
|
||||
|
||||
user_name: Mapped[str] = mapped_column(ForeignKey('users.name'), primary_key=True)
|
||||
class UserProducts(Base):
|
||||
__tablename__ = "user_products"
|
||||
|
||||
user_name: Mapped[str] = mapped_column(ForeignKey("users.name"), primary_key=True)
|
||||
product_id: Mapped[int] = mapped_column(ForeignKey("products.product_id"), primary_key=True)
|
||||
|
||||
count: Mapped[int] = mapped_column(Integer)
|
||||
|
@ -28,4 +29,3 @@ class UserProducts(Base):
|
|||
|
||||
user: Mapped[User] = relationship()
|
||||
product: Mapped[Product] = relationship()
|
||||
|
||||
|
|
|
@ -11,57 +11,69 @@ from ..menus import *
|
|||
|
||||
random.seed()
|
||||
|
||||
|
||||
def main():
|
||||
if not config.getboolean('general', 'stop_allowed'):
|
||||
if not config.getboolean("general", "stop_allowed"):
|
||||
signal.signal(signal.SIGQUIT, signal.SIG_IGN)
|
||||
|
||||
if not config.getboolean('general', 'stop_allowed'):
|
||||
if not config.getboolean("general", "stop_allowed"):
|
||||
signal.signal(signal.SIGTSTP, signal.SIG_IGN)
|
||||
|
||||
main = MainMenu('Dibbler main menu',
|
||||
items=[BuyMenu(),
|
||||
ProductListMenu(),
|
||||
ShowUserMenu(),
|
||||
UserListMenu(),
|
||||
AdjustCreditMenu(),
|
||||
TransferMenu(),
|
||||
AddStockMenu(),
|
||||
Menu('Add/edit',
|
||||
items=[AddUserMenu(),
|
||||
EditUserMenu(),
|
||||
AddProductMenu(),
|
||||
EditProductMenu(),
|
||||
AdjustStockMenu(),
|
||||
CleanupStockMenu(), ]),
|
||||
ProductSearchMenu(),
|
||||
Menu('Statistics',
|
||||
items=[ProductPopularityMenu(),
|
||||
ProductRevenueMenu(),
|
||||
BalanceMenu(),
|
||||
LoggedStatisticsMenu()]),
|
||||
FAQMenu(),
|
||||
PrintLabelMenu()
|
||||
],
|
||||
exit_msg='happy happy joy joy',
|
||||
exit_confirm_msg='Really quit Dibbler?')
|
||||
if not config.getboolean('general', 'quit_allowed'):
|
||||
main.exit_disallowed_msg = 'You can check out any time you like, but you can never leave.'
|
||||
main = MainMenu(
|
||||
"Dibbler main menu",
|
||||
items=[
|
||||
BuyMenu(),
|
||||
ProductListMenu(),
|
||||
ShowUserMenu(),
|
||||
UserListMenu(),
|
||||
AdjustCreditMenu(),
|
||||
TransferMenu(),
|
||||
AddStockMenu(),
|
||||
Menu(
|
||||
"Add/edit",
|
||||
items=[
|
||||
AddUserMenu(),
|
||||
EditUserMenu(),
|
||||
AddProductMenu(),
|
||||
EditProductMenu(),
|
||||
AdjustStockMenu(),
|
||||
CleanupStockMenu(),
|
||||
],
|
||||
),
|
||||
ProductSearchMenu(),
|
||||
Menu(
|
||||
"Statistics",
|
||||
items=[
|
||||
ProductPopularityMenu(),
|
||||
ProductRevenueMenu(),
|
||||
BalanceMenu(),
|
||||
LoggedStatisticsMenu(),
|
||||
],
|
||||
),
|
||||
FAQMenu(),
|
||||
PrintLabelMenu(),
|
||||
],
|
||||
exit_msg="happy happy joy joy",
|
||||
exit_confirm_msg="Really quit Dibbler?",
|
||||
)
|
||||
if not config.getboolean("general", "quit_allowed"):
|
||||
main.exit_disallowed_msg = "You can check out any time you like, but you can never leave."
|
||||
while True:
|
||||
# noinspection PyBroadException
|
||||
try:
|
||||
main.execute()
|
||||
except KeyboardInterrupt:
|
||||
print('')
|
||||
print('Interrupted.')
|
||||
print("")
|
||||
print("Interrupted.")
|
||||
except:
|
||||
print('Something went wrong.')
|
||||
print(f'{sys.exc_info()[0]}: {sys.exc_info()[1]}')
|
||||
if config.getboolean('general', 'show_tracebacks'):
|
||||
print("Something went wrong.")
|
||||
print(f"{sys.exc_info()[0]}: {sys.exc_info()[1]}")
|
||||
if config.getboolean("general", "show_tracebacks"):
|
||||
traceback.print_tb(sys.exc_info()[2])
|
||||
else:
|
||||
break
|
||||
print('Restarting main menu.')
|
||||
print("Restarting main menu.")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
@ -2,8 +2,10 @@
|
|||
from dibbler.models import Base
|
||||
from dibbler.db import engine
|
||||
|
||||
|
||||
def main():
|
||||
Base.metadata.create_all(engine)
|
||||
Base.metadata.create_all(engine)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
main()
|
||||
|
|
|
@ -3,14 +3,16 @@
|
|||
from dibbler.db import Session
|
||||
from dibbler.models import User
|
||||
|
||||
|
||||
def main():
|
||||
# Start an SQL session
|
||||
session=Session()
|
||||
# Let's find all users with a negative credit
|
||||
slabbedasker=session.query(User).filter(User.credit<0).all()
|
||||
# Start an SQL session
|
||||
session = Session()
|
||||
# Let's find all users with a negative credit
|
||||
slabbedasker = session.query(User).filter(User.credit < 0).all()
|
||||
|
||||
for slubbert in slabbedasker:
|
||||
print(f"{slubbert.name}, {slubbert.credit}")
|
||||
for slubbert in slabbedasker:
|
||||
print(f"{slubbert.name}, {slubbert.credit}")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
@ -8,198 +8,224 @@ import matplotlib.dates as mdates
|
|||
|
||||
from dibbler.lib.statistikkHelpers import *
|
||||
|
||||
|
||||
def getInputType():
|
||||
inp = 0
|
||||
while not (inp == '1' or inp == '2' or inp == '3' or inp == '4'):
|
||||
print('type 1 for user-statistics')
|
||||
print('type 2 for product-statistics')
|
||||
print('type 3 for global-statistics')
|
||||
print('type 4 to enter loop-mode')
|
||||
inp = input('')
|
||||
return int(inp)
|
||||
inp = 0
|
||||
while not (inp == "1" or inp == "2" or inp == "3" or inp == "4"):
|
||||
print("type 1 for user-statistics")
|
||||
print("type 2 for product-statistics")
|
||||
print("type 3 for global-statistics")
|
||||
print("type 4 to enter loop-mode")
|
||||
inp = input("")
|
||||
return int(inp)
|
||||
|
||||
|
||||
def getDateFile(date, n):
|
||||
try:
|
||||
if n==0:
|
||||
inp = input('start date? (yyyy-mm-dd) ')
|
||||
elif n==-1:
|
||||
inp = input('end date? (yyyy-mm-dd) ')
|
||||
year = inp.partition('-')
|
||||
month = year[2].partition('-')
|
||||
return datetime.date(int(year[0]), int(month[0]), int(month[2]))
|
||||
except:
|
||||
print('invalid date, setting start start date')
|
||||
if n==0:
|
||||
print('to date found on first line')
|
||||
elif n==-1:
|
||||
print('to date found on last line')
|
||||
print(date)
|
||||
return datetime.date(int(date.partition('-')[0]), int(date.partition('-')[2].partition('-')[0]), int(date.partition('-')[2].partition('-')[2]))
|
||||
try:
|
||||
if n == 0:
|
||||
inp = input("start date? (yyyy-mm-dd) ")
|
||||
elif n == -1:
|
||||
inp = input("end date? (yyyy-mm-dd) ")
|
||||
year = inp.partition("-")
|
||||
month = year[2].partition("-")
|
||||
return datetime.date(int(year[0]), int(month[0]), int(month[2]))
|
||||
except:
|
||||
print("invalid date, setting start start date")
|
||||
if n == 0:
|
||||
print("to date found on first line")
|
||||
elif n == -1:
|
||||
print("to date found on last line")
|
||||
print(date)
|
||||
return datetime.date(
|
||||
int(date.partition("-")[0]),
|
||||
int(date.partition("-")[2].partition("-")[0]),
|
||||
int(date.partition("-")[2].partition("-")[2]),
|
||||
)
|
||||
|
||||
|
||||
def dateToDateNumFile(date, startDate):
|
||||
year = date.partition('-')
|
||||
month = year[2].partition('-')
|
||||
day = datetime.date(int(year[0]), int(month[0]), int(month[2]))
|
||||
deltaDays = day-startDate
|
||||
return int(deltaDays.days), day.weekday()
|
||||
year = date.partition("-")
|
||||
month = year[2].partition("-")
|
||||
day = datetime.date(int(year[0]), int(month[0]), int(month[2]))
|
||||
deltaDays = day - startDate
|
||||
return int(deltaDays.days), day.weekday()
|
||||
|
||||
|
||||
def getProducts(products):
|
||||
product = []
|
||||
products = products.partition('¤')
|
||||
product.append(products[0])
|
||||
while (products[1]=='¤'):
|
||||
products = products[2].partition('¤')
|
||||
product = []
|
||||
products = products.partition("¤")
|
||||
product.append(products[0])
|
||||
return product
|
||||
while products[1] == "¤":
|
||||
products = products[2].partition("¤")
|
||||
product.append(products[0])
|
||||
return product
|
||||
|
||||
|
||||
def piePlot(dictionary, n):
|
||||
keys = []
|
||||
values = []
|
||||
i=0
|
||||
for key in sorted(dictionary, key=dictionary.get, reverse=True):
|
||||
values.append(dictionary[key])
|
||||
if i<n:
|
||||
keys.append(key)
|
||||
i += 1
|
||||
else:
|
||||
keys.append('')
|
||||
plt.pie(values, labels=keys)
|
||||
keys = []
|
||||
values = []
|
||||
i = 0
|
||||
for key in sorted(dictionary, key=dictionary.get, reverse=True):
|
||||
values.append(dictionary[key])
|
||||
if i < n:
|
||||
keys.append(key)
|
||||
i += 1
|
||||
else:
|
||||
keys.append("")
|
||||
plt.pie(values, labels=keys)
|
||||
|
||||
|
||||
def datePlot(array, dateLine):
|
||||
if (not array == []):
|
||||
plt.bar(dateLine, array)
|
||||
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%b'))
|
||||
if not array == []:
|
||||
plt.bar(dateLine, array)
|
||||
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter("%b"))
|
||||
|
||||
|
||||
def dayPlot(array, days):
|
||||
if (not array == []):
|
||||
for i in range(7):
|
||||
array[i]=array[i]*7.0/days
|
||||
plt.bar(list(range(7)), array)
|
||||
plt.xticks(list(range(7)),[' mon',' tue',' wed',' thu',' fri',' sat',' sun'])
|
||||
if not array == []:
|
||||
for i in range(7):
|
||||
array[i] = array[i] * 7.0 / days
|
||||
plt.bar(list(range(7)), array)
|
||||
plt.xticks(
|
||||
list(range(7)),
|
||||
[
|
||||
" mon",
|
||||
" tue",
|
||||
" wed",
|
||||
" thu",
|
||||
" fri",
|
||||
" sat",
|
||||
" sun",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
def graphPlot(array, dateLine):
|
||||
if (not array == []):
|
||||
plt.plot(dateLine, array)
|
||||
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%b'))
|
||||
if not array == []:
|
||||
plt.plot(dateLine, array)
|
||||
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter("%b"))
|
||||
|
||||
|
||||
def plotUser(database, dateLine, user, n):
|
||||
printUser(database, dateLine, user, n)
|
||||
plt.subplot(221)
|
||||
piePlot(database.personVareAntall[user], n)
|
||||
plt.xlabel('antall varer kjøpt gjengitt i antall')
|
||||
plt.subplot(222)
|
||||
datePlot(database.personDatoVerdi[user], dateLine)
|
||||
plt.xlabel('penger brukt over dato')
|
||||
plt.subplot(223)
|
||||
piePlot(database.personVareVerdi[user], n)
|
||||
plt.xlabel('antall varer kjøpt gjengitt i verdi')
|
||||
plt.subplot(224)
|
||||
dayPlot(database.personUkedagVerdi[user], len(dateLine))
|
||||
plt.xlabel('forbruk over ukedager')
|
||||
plt.show()
|
||||
printUser(database, dateLine, user, n)
|
||||
plt.subplot(221)
|
||||
piePlot(database.personVareAntall[user], n)
|
||||
plt.xlabel("antall varer kjøpt gjengitt i antall")
|
||||
plt.subplot(222)
|
||||
datePlot(database.personDatoVerdi[user], dateLine)
|
||||
plt.xlabel("penger brukt over dato")
|
||||
plt.subplot(223)
|
||||
piePlot(database.personVareVerdi[user], n)
|
||||
plt.xlabel("antall varer kjøpt gjengitt i verdi")
|
||||
plt.subplot(224)
|
||||
dayPlot(database.personUkedagVerdi[user], len(dateLine))
|
||||
plt.xlabel("forbruk over ukedager")
|
||||
plt.show()
|
||||
|
||||
|
||||
def plotProduct(database, dateLine, product, n):
|
||||
printProduct(database, dateLine, product, n)
|
||||
plt.subplot(221)
|
||||
piePlot(database.varePersonAntall[product], n)
|
||||
plt.xlabel('personer som har handler produktet')
|
||||
plt.subplot(222)
|
||||
datePlot(database.vareDatoAntall[product], dateLine)
|
||||
plt.xlabel('antall produkter handlet per dag')
|
||||
#plt.subplot(223)
|
||||
plt.subplot(224)
|
||||
dayPlot(database.vareUkedagAntall[product], len(dateLine))
|
||||
plt.xlabel('antall over ukedager')
|
||||
plt.show()
|
||||
printProduct(database, dateLine, product, n)
|
||||
plt.subplot(221)
|
||||
piePlot(database.varePersonAntall[product], n)
|
||||
plt.xlabel("personer som har handler produktet")
|
||||
plt.subplot(222)
|
||||
datePlot(database.vareDatoAntall[product], dateLine)
|
||||
plt.xlabel("antall produkter handlet per dag")
|
||||
# plt.subplot(223)
|
||||
plt.subplot(224)
|
||||
dayPlot(database.vareUkedagAntall[product], len(dateLine))
|
||||
plt.xlabel("antall over ukedager")
|
||||
plt.show()
|
||||
|
||||
|
||||
def plotGlobal(database, dateLine, n):
|
||||
printGlobal(database, dateLine, n)
|
||||
plt.subplot(231)
|
||||
piePlot(database.globalVareVerdi, n)
|
||||
plt.xlabel('varer kjøpt gjengitt som verdi')
|
||||
plt.subplot(232)
|
||||
datePlot(database.globalDatoForbruk, dateLine)
|
||||
plt.xlabel('forbruk over dato')
|
||||
plt.subplot(233)
|
||||
graphPlot(database.pengebeholdning, dateLine)
|
||||
plt.xlabel('pengebeholdning over tid (negativ verdi utgjør samlet kreditt)')
|
||||
plt.subplot(234)
|
||||
piePlot(database.globalPersonForbruk, n)
|
||||
plt.xlabel('penger brukt av personer')
|
||||
plt.subplot(235)
|
||||
dayPlot(database.globalUkedagForbruk, len(dateLine))
|
||||
plt.xlabel('forbruk over ukedager')
|
||||
plt.show()
|
||||
printGlobal(database, dateLine, n)
|
||||
plt.subplot(231)
|
||||
piePlot(database.globalVareVerdi, n)
|
||||
plt.xlabel("varer kjøpt gjengitt som verdi")
|
||||
plt.subplot(232)
|
||||
datePlot(database.globalDatoForbruk, dateLine)
|
||||
plt.xlabel("forbruk over dato")
|
||||
plt.subplot(233)
|
||||
graphPlot(database.pengebeholdning, dateLine)
|
||||
plt.xlabel("pengebeholdning over tid (negativ verdi utgjør samlet kreditt)")
|
||||
plt.subplot(234)
|
||||
piePlot(database.globalPersonForbruk, n)
|
||||
plt.xlabel("penger brukt av personer")
|
||||
plt.subplot(235)
|
||||
dayPlot(database.globalUkedagForbruk, len(dateLine))
|
||||
plt.xlabel("forbruk over ukedager")
|
||||
plt.show()
|
||||
|
||||
|
||||
def alt4menu(database, dateLine, useDatabase):
|
||||
n=10
|
||||
while 1:
|
||||
print('\n1: user-statistics, 2: product-statistics, 3:global-statistics, n: adjust amount of data shown q:quit')
|
||||
try:
|
||||
inp = input('')
|
||||
except:
|
||||
continue
|
||||
if inp == 'q':
|
||||
break
|
||||
elif inp == '1':
|
||||
if i=='0':
|
||||
user = input('input full username: ')
|
||||
else:
|
||||
user = getUser()
|
||||
plotUser(database, dateLine, user, n)
|
||||
elif inp == '2':
|
||||
if i=='0':
|
||||
product = input('input full product name: ')
|
||||
else:
|
||||
product = getProduct()
|
||||
plotProduct(database, dateLine, product, n)
|
||||
elif inp == '3':
|
||||
plotGlobal(database, dateLine, n)
|
||||
elif inp == 'n':
|
||||
try:
|
||||
n=int(input('set number to show '));
|
||||
except:
|
||||
pass
|
||||
n = 10
|
||||
while 1:
|
||||
print(
|
||||
"\n1: user-statistics, 2: product-statistics, 3:global-statistics, n: adjust amount of data shown q:quit"
|
||||
)
|
||||
try:
|
||||
inp = input("")
|
||||
except:
|
||||
continue
|
||||
if inp == "q":
|
||||
break
|
||||
elif inp == "1":
|
||||
if i == "0":
|
||||
user = input("input full username: ")
|
||||
else:
|
||||
user = getUser()
|
||||
plotUser(database, dateLine, user, n)
|
||||
elif inp == "2":
|
||||
if i == "0":
|
||||
product = input("input full product name: ")
|
||||
else:
|
||||
product = getProduct()
|
||||
plotProduct(database, dateLine, product, n)
|
||||
elif inp == "3":
|
||||
plotGlobal(database, dateLine, n)
|
||||
elif inp == "n":
|
||||
try:
|
||||
n = int(input("set number to show "))
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
def main():
|
||||
inputType=getInputType()
|
||||
i = input('0:fil, 1:database \n? ')
|
||||
if (inputType == 1):
|
||||
if i=='0':
|
||||
user = input('input full username: ')
|
||||
inputType = getInputType()
|
||||
i = input("0:fil, 1:database \n? ")
|
||||
if inputType == 1:
|
||||
if i == "0":
|
||||
user = input("input full username: ")
|
||||
else:
|
||||
user = getUser()
|
||||
product = ""
|
||||
elif inputType == 2:
|
||||
if i == "0":
|
||||
product = input("input full product name: ")
|
||||
else:
|
||||
product = getProduct()
|
||||
user = ""
|
||||
else:
|
||||
user = getUser()
|
||||
product = ''
|
||||
elif (inputType == 2):
|
||||
if i=='0':
|
||||
product = input('input full product name: ')
|
||||
product = ""
|
||||
user = ""
|
||||
if i == "0":
|
||||
inputFile = input("logfil? ")
|
||||
if inputFile == "":
|
||||
inputFile = "default.dibblerlog"
|
||||
database, dateLine = buildDatabaseFromFile(inputFile, inputType, product, user)
|
||||
else:
|
||||
product = getProduct()
|
||||
user = ''
|
||||
else :
|
||||
product = ''
|
||||
user = ''
|
||||
if i=='0':
|
||||
inputFile=input('logfil? ')
|
||||
if inputFile=='':
|
||||
inputFile='default.dibblerlog'
|
||||
database, dateLine = buildDatabaseFromFile(inputFile, inputType, product, user)
|
||||
else:
|
||||
database, dateLine = buildDatabaseFromDb(inputType, product, user)
|
||||
database, dateLine = buildDatabaseFromDb(inputType, product, user)
|
||||
|
||||
if (inputType==1):
|
||||
plotUser(database, dateLine, user, 10)
|
||||
if (inputType==2):
|
||||
plotProduct(database, dateLine, product, 10)
|
||||
if (inputType==3):
|
||||
plotGlobal(database, dateLine, 10)
|
||||
if (inputType==4):
|
||||
alt4menu(database, dateLine, i)
|
||||
if inputType == 1:
|
||||
plotUser(database, dateLine, user, 10)
|
||||
if inputType == 2:
|
||||
plotProduct(database, dateLine, product, 10)
|
||||
if inputType == 3:
|
||||
plotGlobal(database, dateLine, 10)
|
||||
if inputType == 4:
|
||||
alt4menu(database, dateLine, i)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
Loading…
Reference in New Issue