format code with black
This commit is contained in:
@@ -21,8 +21,8 @@ from .miscmenus import (
|
||||
)
|
||||
from .printermenu import PrintLabelMenu
|
||||
from .stats import (
|
||||
ProductPopularityMenu,
|
||||
ProductRevenueMenu,
|
||||
BalanceMenu,
|
||||
LoggedStatisticsMenu
|
||||
ProductPopularityMenu,
|
||||
ProductRevenueMenu,
|
||||
BalanceMenu,
|
||||
LoggedStatisticsMenu,
|
||||
)
|
||||
|
||||
+51
-27
@@ -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}")
|
||||
|
||||
+59
-42
@@ -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
|
||||
|
||||
|
||||
+95
-66
@@ -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
|
||||
|
||||
|
||||
+51
-30
@@ -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.
|
||||
''')]
|
||||
""",
|
||||
),
|
||||
]
|
||||
|
||||
+168
-84
@@ -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
|
||||
|
||||
+94
-72
@@ -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",
|
||||
)
|
||||
|
||||
+71
-47
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user