Rewrite input_str to allow defaults and act less strange with some parameters
This commit is contained in:
parent
1675f26080
commit
ca19cc14da
|
@ -101,7 +101,7 @@ much money you're due in credits for the purchase when prompted.\n'''
|
|||
else:
|
||||
self.price = price
|
||||
|
||||
description = self.input_str('Log message> ', length_range=(0, 50))
|
||||
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:
|
||||
|
|
|
@ -11,10 +11,10 @@ class AddUserMenu(Menu):
|
|||
|
||||
def _execute(self):
|
||||
self.print_header()
|
||||
username = self.input_str('Username (should be same as PVV username)> ', User.name_re, (1, 10))
|
||||
cardnum = self.input_str('Card number (optional)> ', User.card_re, (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)> ', User.rfid_re, (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:
|
||||
|
@ -37,14 +37,14 @@ 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> ')
|
||||
user = self.input_user('User')
|
||||
self.printc(f'Editing user {user.name}')
|
||||
card_str = f'"{user.card}"'
|
||||
if user.card is None:
|
||||
card_str = 'empty'
|
||||
# TODO: Inconsistent with other defaulted strings. Redo.
|
||||
user.card = self.input_str('Card number (currently %s)> ' % card_str,
|
||||
User.card_re, (0, 10),
|
||||
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()
|
||||
|
@ -53,8 +53,8 @@ user, then rfid (write an empty line to remove the card number or rfid).
|
|||
if user.rfid is None:
|
||||
rfid_str = 'empty'
|
||||
# TODO: Inconsistent with other defaulted strings. Redo.
|
||||
user.rfid = self.input_str(f'RFID (currently {rfid_str})> ',
|
||||
User.rfid_re, (0, 10),
|
||||
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()
|
||||
|
@ -70,9 +70,9 @@ class AddProductMenu(Menu):
|
|||
|
||||
def _execute(self):
|
||||
self.print_header()
|
||||
bar_code = self.input_str('Bar code> ', Product.bar_code_re, (8, 13))
|
||||
name = self.input_str('Name> ', Product.name_re, (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', allowed_range=(1, 100000))
|
||||
product = Product(bar_code, name, price)
|
||||
self.session.add(product)
|
||||
try:
|
||||
|
@ -89,7 +89,7 @@ class EditProductMenu(Menu):
|
|||
|
||||
def _execute(self):
|
||||
self.print_header()
|
||||
product = self.input_product('Product> ')
|
||||
product = self.input_product('Product')
|
||||
self.printc(f'Editing product {product.name}')
|
||||
while True:
|
||||
selector = Selector(f'Do what with {product.name}?',
|
||||
|
@ -100,11 +100,13 @@ class EditProductMenu(Menu):
|
|||
('store', 'Store')])
|
||||
what = selector.execute()
|
||||
if what == 'name':
|
||||
product.name = self.input_str(f'Name[{product.name}]> ', Product.name_re, (1, product.name_length))
|
||||
product.name = self.input_str(f'Name[{product.name}]', regex=Product.name_re,
|
||||
length_range=(1, product.name_length))
|
||||
elif what == 'price':
|
||||
product.price = self.input_int(f'Price[{product.price}]> ', (1, 100000))
|
||||
product.price = self.input_int(f'Price[{product.price}]', allowed_range=(1, 100000))
|
||||
elif what == 'barcode':
|
||||
product.bar_code = self.input_str(f'Bar code[{product.bar_code}]> ', Product.bar_code_re, (8, 13))
|
||||
product.bar_code = self.input_str(f'Bar code[{product.bar_code}]', regex=Product.bar_code_re,
|
||||
length_range=(8, 13))
|
||||
elif what == 'hidden':
|
||||
product.hidden = self.confirm('Hidden[%s]' % ("Y" if product.hidden else "N"), False)
|
||||
elif what == 'store':
|
||||
|
@ -128,19 +130,17 @@ class AdjustStockMenu(Menu):
|
|||
|
||||
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))
|
||||
add_stock = self.input_int('Added stock', allowed_range=(-1000, 1000))
|
||||
# TODO: Print something else when adding negative stock?
|
||||
print(f'You added {add_stock:d} to the stock of {product}')
|
||||
|
||||
product.stock += add_stock
|
||||
|
||||
print(f'The stock is now {product.stock:d}')
|
||||
|
||||
try:
|
||||
self.session.commit()
|
||||
print('Stock is now stored')
|
||||
|
@ -172,7 +172,7 @@ class CleanupStockMenu(Menu):
|
|||
|
||||
for product in products:
|
||||
oldstock = product.stock
|
||||
product.stock = self.input_int(product.name, (0, 10000), default=max(0, oldstock))
|
||||
product.stock = self.input_int(product.name, allowed_range=(0, 10000), default=max(0, oldstock))
|
||||
self.session.add(product)
|
||||
if oldstock != product.stock:
|
||||
changed_products.append((product, oldstock))
|
||||
|
|
|
@ -10,8 +10,7 @@ from select import select
|
|||
import conf
|
||||
from db import User, Session
|
||||
from helpers import search_user, search_product, guess_data_type, argmax
|
||||
from text_interface import context_commands, local_help_commands, help_commands, \
|
||||
exit_commands
|
||||
from text_interface import context_commands, local_help_commands, help_commands, exit_commands
|
||||
|
||||
|
||||
class ExitMenu(Exception):
|
||||
|
@ -19,13 +18,14 @@ class ExitMenu(Exception):
|
|||
|
||||
|
||||
class Menu(object):
|
||||
def __init__(self, name, items=None, prompt='> ',
|
||||
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
|
||||
self.end_prompt = end_prompt
|
||||
self.return_index = return_index
|
||||
self.exit_msg = exit_msg
|
||||
self.exit_confirm_msg = exit_confirm_msg
|
||||
|
@ -86,36 +86,18 @@ class Menu(object):
|
|||
return i
|
||||
return self.items[i]
|
||||
|
||||
# TODO: Allow default
|
||||
def input_str(self, prompt=None, regex=None, length_range=(None, None),
|
||||
empty_string_is_none=False, timeout=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 regex is not None:
|
||||
while True:
|
||||
result = self.input_str(prompt, length_range=length_range,
|
||||
empty_string_is_none=empty_string_is_none)
|
||||
if result is None or re.match(regex + '$', result):
|
||||
return result
|
||||
prompt = self.prompt if self.prompt is not None else ""
|
||||
if default is not None:
|
||||
prompt += f" [{default}]"
|
||||
if end_prompt is not None:
|
||||
prompt += end_prompt
|
||||
elif self.end_prompt is not None:
|
||||
prompt += self.end_prompt
|
||||
else:
|
||||
print(f'Value must match regular expression "{regex}"')
|
||||
if length_range != (None, None):
|
||||
while True:
|
||||
result = self.input_str(prompt, empty_string_is_none=empty_string_is_none)
|
||||
if result is None:
|
||||
length = 0
|
||||
else:
|
||||
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_range[1]:
|
||||
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}')
|
||||
else:
|
||||
print(f'Value must have length at most {length_range[1]:d}')
|
||||
else:
|
||||
return result
|
||||
prompt += " "
|
||||
while True:
|
||||
try:
|
||||
if timeout:
|
||||
|
@ -127,7 +109,7 @@ class Menu(object):
|
|||
# timeout occurred, simulate empty line
|
||||
result = ''
|
||||
else:
|
||||
result = input().strip()
|
||||
result = input(prompt).strip()
|
||||
else:
|
||||
result = input(prompt).strip()
|
||||
except EOFError:
|
||||
|
@ -150,6 +132,21 @@ class Menu(object):
|
|||
continue
|
||||
if empty_string_is_none and result == '':
|
||||
return None
|
||||
if default is not None and result == '':
|
||||
return default
|
||||
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_range[1]:
|
||||
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}')
|
||||
else:
|
||||
print(f'Value must have length at most {length_range[1]:d}')
|
||||
continue
|
||||
return result
|
||||
|
||||
def special_input_options(self, result):
|
||||
|
@ -172,13 +169,12 @@ class Menu(object):
|
|||
"""
|
||||
return False
|
||||
|
||||
def input_choice(self, number_of_choices, prompt=None):
|
||||
if prompt is None:
|
||||
prompt = self.prompt
|
||||
def input_choice(self, number_of_choices, prompt=None, end_prompt=None):
|
||||
while True:
|
||||
result = self.input_str(prompt)
|
||||
result = self.input_str(prompt, end_prompt)
|
||||
if result == '':
|
||||
print('Please enter something')
|
||||
#TODO: Move this into mainmenu.py special options
|
||||
# 'c' in main menu to change colours
|
||||
elif result == 'c':
|
||||
os.system('echo -e "\033[' + str(random.randint(40, 49)) + ';' + str(random.randint(30, 37)) + ';5m"')
|
||||
|
@ -204,21 +200,14 @@ class Menu(object):
|
|||
def invalid_menu_choice(self, in_str):
|
||||
print('Please enter a valid choice.')
|
||||
|
||||
def input_int(self, prompt=None, allowed_range=(None, None), null_allowed=False, default=None):
|
||||
# TODO: Proper default handling
|
||||
if prompt is None:
|
||||
prompt = self.prompt
|
||||
def input_int(self, prompt=None, end_prompt=None, allowed_range=(None, None), null_allowed=False, default=None):
|
||||
while True:
|
||||
result = self.input_str(prompt)
|
||||
if result == '':
|
||||
if default is not None:
|
||||
return default
|
||||
elif null_allowed:
|
||||
result = self.input_str(prompt, end_prompt, default=default)
|
||||
if result == '' and null_allowed:
|
||||
return False
|
||||
try:
|
||||
value = int(result)
|
||||
if ((allowed_range[0] and value < allowed_range[0]) or
|
||||
(allowed_range[1] and value > allowed_range[1])):
|
||||
if ((allowed_range[0] and value < allowed_range[0]) or (allowed_range[1] and value > allowed_range[1])):
|
||||
if allowed_range[0] and allowed_range[1]:
|
||||
print(f'Value must be in range [{allowed_range[0]:d}, {allowed_range[1]:d}]')
|
||||
elif allowed_range[0]:
|
||||
|
@ -230,40 +219,40 @@ class Menu(object):
|
|||
except ValueError:
|
||||
print("Please enter an integer")
|
||||
|
||||
def input_user(self, prompt=None):
|
||||
def input_user(self, prompt=None, end_prompt=None):
|
||||
user = None
|
||||
while user is None:
|
||||
user = self.retrieve_user(self.input_str(prompt))
|
||||
user = self.retrieve_user(self.input_str(prompt, end_prompt))
|
||||
return user
|
||||
|
||||
def retrieve_user(self, search_str):
|
||||
return self.search_ui(search_user, search_str, 'user')
|
||||
|
||||
def input_product(self, prompt=None):
|
||||
def input_product(self, prompt=None, end_prompt=None):
|
||||
product = None
|
||||
while product is None:
|
||||
product = self.retrieve_product(self.input_str(prompt))
|
||||
product = self.retrieve_product(self.input_str(prompt, end_prompt))
|
||||
return product
|
||||
|
||||
def retrieve_product(self, search_str):
|
||||
return self.search_ui(search_product, search_str, 'product')
|
||||
|
||||
def input_thing(self, prompt=None, permitted_things=('user', 'product'),
|
||||
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)
|
||||
search_str = self.input_str(prompt, end_prompt)
|
||||
if search_str == '' and empty_input_permitted:
|
||||
return None
|
||||
result = self.search_for_thing(search_str, permitted_things, add_nonexisting, find_hidden_products)
|
||||
return result
|
||||
|
||||
def input_multiple(self, prompt=None, permitted_things=('user', 'product'),
|
||||
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)
|
||||
search_str = self.input_str(prompt, end_prompt)
|
||||
search_lst = search_str.split(" ")
|
||||
if search_str == '' and empty_input_permitted:
|
||||
return None
|
||||
|
@ -330,20 +319,20 @@ class Menu(object):
|
|||
('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)> ',
|
||||
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> ')
|
||||
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})')
|
||||
return user
|
||||
return None
|
||||
if type_guess == 'bar_code':
|
||||
print('"%s" looks like the bar code for a product, but no such product exists.' % string)
|
||||
print(f'"{string}" looks like the bar code for a product, but no such product exists.')
|
||||
return None
|
||||
|
||||
def search_ui(self, search_fun, search_str, thing):
|
||||
|
@ -373,8 +362,8 @@ class Menu(object):
|
|||
return selector.execute()
|
||||
|
||||
@staticmethod
|
||||
def confirm(prompt, default=None, timeout=None):
|
||||
return ConfirmMenu(prompt, default, timeout).execute()
|
||||
def confirm(prompt, end_prompt=None, default=None, timeout=None):
|
||||
return ConfirmMenu(prompt, end_prompt=None, default=default, timeout=timeout).execute()
|
||||
|
||||
def header(self):
|
||||
return f"[{self.name}]"
|
||||
|
@ -384,7 +373,7 @@ class Menu(object):
|
|||
print(self.header())
|
||||
|
||||
def pause(self):
|
||||
self.input_str('.')
|
||||
self.input_str('.', end_prompt="")
|
||||
|
||||
@staticmethod
|
||||
def general_help():
|
||||
|
@ -445,7 +434,7 @@ class Menu(object):
|
|||
return None
|
||||
for i in range(len(self.items)):
|
||||
self.printc(line_format % (i + 1, self.item_name(i)))
|
||||
item_i = self.input_choice(len(self.items), prompt=self.prompt) - 1
|
||||
item_i = self.input_choice(len(self.items)) - 1
|
||||
if self.item_is_submenu(item_i):
|
||||
self.items[item_i].execute()
|
||||
else:
|
||||
|
@ -467,8 +456,8 @@ class MessageMenu(Menu):
|
|||
|
||||
|
||||
class ConfirmMenu(Menu):
|
||||
def __init__(self, prompt='confirm?', default=None, timeout=0):
|
||||
Menu.__init__(self, 'question', prompt=prompt,
|
||||
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
|
||||
|
@ -476,7 +465,7 @@ class ConfirmMenu(Menu):
|
|||
def _execute(self):
|
||||
options = {True: '[y]/n', False: 'y/[n]', None: 'y/n'}[self.default]
|
||||
while True:
|
||||
result = self.input_str(f'{self.prompt} ({options}): ', 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']:
|
||||
return True
|
||||
|
@ -489,11 +478,11 @@ class ConfirmMenu(Menu):
|
|||
|
||||
|
||||
class Selector(Menu):
|
||||
def __init__(self, name, items=None, prompt='select> ', return_index=True, exit_msg=None, exit_confirm_msg=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, exit_msg)
|
||||
Menu.__init__(self, name, items, prompt, return_index=return_index, exit_msg=exit_msg)
|
||||
|
||||
def header(self):
|
||||
return self.name
|
||||
|
|
|
@ -12,13 +12,13 @@ class TransferMenu(Menu):
|
|||
|
||||
def _execute(self):
|
||||
self.print_header()
|
||||
amount = self.input_int('Transfer amount> ', (1, 100000))
|
||||
amount = self.input_int('Transfer amount', (1, 100000))
|
||||
self.set_context(f'Transferring {amount:d} kr', display=False)
|
||||
user1 = self.input_user('From user> ')
|
||||
user1 = self.input_user('From user')
|
||||
self.add_to_context(f' from {user1.name}')
|
||||
user2 = self.input_user('To user> ')
|
||||
user2 = self.input_user('To user')
|
||||
self.add_to_context(f' to {user2.name}')
|
||||
comment = self.input_str('Comment> ')
|
||||
comment = self.input_str('Comment')
|
||||
self.add_to_context(f' (comment) {user2.name}')
|
||||
|
||||
t1 = Transaction(user1, amount,
|
||||
|
@ -46,7 +46,7 @@ class ShowUserMenu(Menu):
|
|||
|
||||
def _execute(self):
|
||||
self.print_header()
|
||||
user = self.input_user('User name, card number or RFID> ')
|
||||
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}')
|
||||
|
@ -159,17 +159,17 @@ class AdjustCreditMenu(Menu):
|
|||
|
||||
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))
|
||||
amount = self.input_int('Add amount', allowed_range=(-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))
|
||||
description = self.input_str('Log message', length_range=(0, 50))
|
||||
if description == '':
|
||||
description = 'manually adjusted credit'
|
||||
transaction = Transaction(user, -amount, description)
|
||||
|
|
|
@ -18,7 +18,7 @@ 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):
|
||||
|
|
Loading…
Reference in New Issue