format code with black

This commit is contained in:
2023-08-29 23:50:34 +02:00
parent 88d855a951
commit c219fa61ba
30 changed files with 1514 additions and 1124 deletions
+4 -4
View File
@@ -21,8 +21,8 @@ from .miscmenus import (
)
from .printermenu import PrintLabelMenu
from .stats import (
ProductPopularityMenu,
ProductRevenueMenu,
BalanceMenu,
LoggedStatisticsMenu
ProductPopularityMenu,
ProductRevenueMenu,
BalanceMenu,
LoggedStatisticsMenu,
)
+51 -27
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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)
+15 -9
View File
@@ -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
View File
@@ -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 -9
View File
@@ -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
View File
@@ -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()