Lagt til diverse tant og fjas.
* Menu-klassen utvidet med kode for å vise menyen og velge ting fra den, samt litt mer generelle funksjoner for å lese input * Ny klasse Selector for «små» menyer som bare er for å velge en verdi (disse skal ikke ha undermenyer) * Nye menyer: ShowUserMenu, BuyMenu (foreløpig med kun innlesing av data, ikke lagring), ProductListMenu * Forsøk på «intelligent» håndtering av input i BuyMenu (se funksjonene dwim_search og guess_data_type) * La inn to tøyseprodukter i datafilen for å ha noen produkter å teste med
This commit is contained in:
parent
f71ac4d348
commit
9b222debf2
6
db.py
6
db.py
|
@ -21,6 +21,9 @@ class User(Base):
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<User('%s')>" % self.name
|
return "<User('%s')>" % self.name
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
class Product(Base):
|
class Product(Base):
|
||||||
__tablename__ = 'products'
|
__tablename__ = 'products'
|
||||||
|
|
||||||
|
@ -36,6 +39,9 @@ class Product(Base):
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<Product('%s', '%s', '%s')>" % (self.name, self.bar_code, self.price)
|
return "<Product('%s', '%s', '%s')>" % (self.name, self.bar_code, self.price)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
class PurchaseEntry(Base):
|
class PurchaseEntry(Base):
|
||||||
__tablename__ = 'purchase_entries'
|
__tablename__ = 'purchase_entries'
|
||||||
id = Column(Integer, primary_key=True)
|
id = Column(Integer, primary_key=True)
|
||||||
|
|
32
helpers.py
32
helpers.py
|
@ -1,6 +1,34 @@
|
||||||
from db import *
|
from db import *
|
||||||
from sqlalchemy import or_
|
from sqlalchemy import or_
|
||||||
|
|
||||||
|
def search_user(string, session):
|
||||||
|
exact_match = session.query(User).filter(or_(User.name==string, User.card==string)).first()
|
||||||
|
if exact_match:
|
||||||
|
return exact_match
|
||||||
|
user_list = session.query(User).filter(or_(User.name.like('%'+string+'%'),User.card.like('%'+string+'%'))).all()
|
||||||
|
return user_list
|
||||||
|
|
||||||
|
def search_product(string, session):
|
||||||
|
exact_match = session.query(Product)\
|
||||||
|
.filter(or_(Product.bar_code==string,
|
||||||
|
Product.name==string)).first()
|
||||||
|
if exact_match:
|
||||||
|
return exact_match
|
||||||
|
product_list = session.query(Product)\
|
||||||
|
.filter(or_(Product.bar_code.like('%'+string+'%'),
|
||||||
|
Product.name.like('%'+string+'%'))).all()
|
||||||
|
return product_list
|
||||||
|
|
||||||
|
def guess_data_type(string):
|
||||||
|
if string.startswith('NTNU'):
|
||||||
|
return 'card'
|
||||||
|
if string.isdigit():
|
||||||
|
return 'bar_code'
|
||||||
|
if string.isalpha() and string.islower():
|
||||||
|
return 'username'
|
||||||
|
return 'product_name'
|
||||||
|
|
||||||
|
|
||||||
def retrieve_user(string, session):
|
def retrieve_user(string, session):
|
||||||
first = session.query(User).filter(or_(User.name==string, User.card==string)).first()
|
first = session.query(User).filter(or_(User.name==string, User.card==string)).first()
|
||||||
if first:
|
if first:
|
||||||
|
@ -22,9 +50,9 @@ def retrieve_user(string, session):
|
||||||
return select_from_list(list)
|
return select_from_list(list)
|
||||||
|
|
||||||
|
|
||||||
def confirm():
|
def confirm(prompt='Confirm? (y/n) '):
|
||||||
while True:
|
while True:
|
||||||
input = raw_input("Confirm? (y/n)\n")
|
input = raw_input(prompt)
|
||||||
if input in ["y","yes"]:
|
if input in ["y","yes"]:
|
||||||
return True
|
return True
|
||||||
elif input in ["n","no"]:
|
elif input in ["n","no"]:
|
||||||
|
|
226
text_based.py
226
text_based.py
|
@ -1,8 +1,100 @@
|
||||||
from helpers import *
|
from helpers import *
|
||||||
|
|
||||||
|
exit_commands = ['exit', 'abort', 'quit']
|
||||||
|
|
||||||
|
class ExitMenu(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
class Menu():
|
class Menu():
|
||||||
def __init__(self, name):
|
def __init__(self, name, items=[], prompt='> ', exit_msg=None):
|
||||||
self.name = name
|
self.name = name
|
||||||
|
self.items = items
|
||||||
|
self.prompt = prompt
|
||||||
|
self.exit_msg = exit_msg
|
||||||
|
|
||||||
|
def at_exit(self):
|
||||||
|
if self.exit_msg:
|
||||||
|
print self.exit_msg
|
||||||
|
|
||||||
|
def item_is_submenu(self, i):
|
||||||
|
return isinstance(self.items[i], Menu)
|
||||||
|
|
||||||
|
def item_name(self, i):
|
||||||
|
if self.item_is_submenu(i):
|
||||||
|
return self.items[i].name
|
||||||
|
else:
|
||||||
|
return str(self.items[i])
|
||||||
|
|
||||||
|
def input_str(self, prompt=None):
|
||||||
|
if prompt == None:
|
||||||
|
prompt = self.prompt
|
||||||
|
try:
|
||||||
|
result = raw_input(prompt)
|
||||||
|
except EOFError:
|
||||||
|
print 'quit'
|
||||||
|
raise ExitMenu()
|
||||||
|
if result in exit_commands:
|
||||||
|
raise ExitMenu()
|
||||||
|
return result
|
||||||
|
|
||||||
|
def input_int(self, prompt=None, allowed_range=(None,None)):
|
||||||
|
if prompt == None:
|
||||||
|
prompt = self.prompt
|
||||||
|
while True:
|
||||||
|
result = self.input_str(prompt)
|
||||||
|
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 allowed_range[1]:
|
||||||
|
print 'Value must be in range [%d,%d]' % allowed_range
|
||||||
|
elif allowed_range[0]:
|
||||||
|
print 'Value must be at least %d' % allowed_range[0]
|
||||||
|
else:
|
||||||
|
print 'Value must be at most %d' % allowed_range[1]
|
||||||
|
else:
|
||||||
|
return value
|
||||||
|
except ValueError:
|
||||||
|
print 'Please enter an integer'
|
||||||
|
|
||||||
|
def print_header(self):
|
||||||
|
print
|
||||||
|
print '[%s]' % self.name
|
||||||
|
|
||||||
|
def pause(self):
|
||||||
|
self.input_str('.')
|
||||||
|
|
||||||
|
def execute(self):
|
||||||
|
try:
|
||||||
|
return self._execute()
|
||||||
|
except ExitMenu:
|
||||||
|
self.at_exit()
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _execute(self):
|
||||||
|
while True:
|
||||||
|
self.print_header()
|
||||||
|
if len(self.items)==0:
|
||||||
|
print '(empty menu)'
|
||||||
|
self.pause()
|
||||||
|
return None
|
||||||
|
for i in range(len(self.items)):
|
||||||
|
print '%d ) %s' % (i+1, self.item_name(i))
|
||||||
|
item_i = self.input_int(self.prompt, (1,len(self.items)))-1
|
||||||
|
if self.item_is_submenu(item_i):
|
||||||
|
self.items[item_i].execute()
|
||||||
|
else:
|
||||||
|
return item_i
|
||||||
|
|
||||||
|
class Selector(Menu):
|
||||||
|
def print_header(self):
|
||||||
|
print self.name
|
||||||
|
|
||||||
|
def _execute(self):
|
||||||
|
result = Menu._execute(self)
|
||||||
|
if result != None:
|
||||||
|
return self.items[result]
|
||||||
|
return None
|
||||||
|
|
||||||
class ChargeMenu(Menu):
|
class ChargeMenu(Menu):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
@ -44,6 +136,88 @@ class ChargeMenu(Menu):
|
||||||
self.session.close()
|
self.session.close()
|
||||||
break
|
break
|
||||||
|
|
||||||
|
class ShowUserMenu(Menu):
|
||||||
|
def __init__(self):
|
||||||
|
Menu.__init__(self, 'Show user')
|
||||||
|
|
||||||
|
def _execute(self):
|
||||||
|
self.print_header()
|
||||||
|
user = None
|
||||||
|
while user == None:
|
||||||
|
search_str = self.input_str('User name or card number> ')
|
||||||
|
user = search_ui(search_user, search_str, 'user', Session())
|
||||||
|
print 'User name: %s' % user.name
|
||||||
|
print 'Card number: %s' % user.card
|
||||||
|
print 'Credit: %s' % user.credit
|
||||||
|
self.pause()
|
||||||
|
|
||||||
|
|
||||||
|
class BuyMenu(Menu):
|
||||||
|
def __init__(self):
|
||||||
|
Menu.__init__(self, 'Buy')
|
||||||
|
|
||||||
|
def _execute(self):
|
||||||
|
self.print_header()
|
||||||
|
session = Session()
|
||||||
|
user = None
|
||||||
|
products = []
|
||||||
|
while True:
|
||||||
|
self.print_partial_purchase(user, products)
|
||||||
|
print {(False,False): 'Enter user or product identification',
|
||||||
|
(False,True): 'Enter user identification or more products',
|
||||||
|
(True,False): 'Enter product identification',
|
||||||
|
(True,True): 'Enter more products, or an empty line to confirm'
|
||||||
|
}[(user != None, len(products) > 0)]
|
||||||
|
string = self.input_str()
|
||||||
|
if string == '':
|
||||||
|
if user == None or len(products) == 0:
|
||||||
|
if confirm('Not enough information entered. Abort purchase? (y/n) '):
|
||||||
|
return False
|
||||||
|
continue
|
||||||
|
break
|
||||||
|
(value_type,value) = dwim_search(string, session)
|
||||||
|
if value != None:
|
||||||
|
if value_type == 'user':
|
||||||
|
user = value
|
||||||
|
elif value_type == 'product':
|
||||||
|
products.append(value)
|
||||||
|
print 'OK purchase'
|
||||||
|
print self.format_partial_purchase(user, products)
|
||||||
|
# TODO build Purchase object and commit
|
||||||
|
self.pause()
|
||||||
|
|
||||||
|
|
||||||
|
def format_partial_purchase(self, user, products):
|
||||||
|
if user == None and len(products) == 0:
|
||||||
|
return
|
||||||
|
strings = []
|
||||||
|
if user != None:
|
||||||
|
strings.append(' user: ' + user.name)
|
||||||
|
if len(products) > 0:
|
||||||
|
strings.append(' products: ' + ', '.join(map(lambda p: p.name, products)))
|
||||||
|
return '\n'.join(strings)
|
||||||
|
|
||||||
|
def print_partial_purchase(self, user, products):
|
||||||
|
info = self.format_partial_purchase(user, products)
|
||||||
|
if info != None:
|
||||||
|
print 'Your purchase:\n' + info
|
||||||
|
|
||||||
|
|
||||||
|
class ProductListMenu(Menu):
|
||||||
|
def __init__(self):
|
||||||
|
Menu.__init__(self, 'Product list')
|
||||||
|
|
||||||
|
def _execute(self):
|
||||||
|
self.print_header()
|
||||||
|
session = Session()
|
||||||
|
product_list = session.query(Product).all()
|
||||||
|
line_format = '%-20s %6s %-15s'
|
||||||
|
print line_format % ('name', 'price', 'bar code')
|
||||||
|
for p in product_list:
|
||||||
|
print line_format % (p.name, p.price, p.bar_code)
|
||||||
|
self.pause()
|
||||||
|
|
||||||
|
|
||||||
class MainMenu():
|
class MainMenu():
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.menu_list = [Menu("Buy"),ChargeMenu(), Menu("Add User"), Menu("Add Product")]
|
self.menu_list = [Menu("Buy"),ChargeMenu(), Menu("Add User"), Menu("Add Product")]
|
||||||
|
@ -62,7 +236,55 @@ class MainMenu():
|
||||||
else:
|
else:
|
||||||
print "This does not make sense"
|
print "This does not make sense"
|
||||||
|
|
||||||
main = MainMenu()
|
|
||||||
|
def dwim_search(string, session):
|
||||||
|
typ = guess_data_type(string)
|
||||||
|
if typ == None:
|
||||||
|
print 'This does not make sense'
|
||||||
|
return
|
||||||
|
retriever = {'card': retrieve_user,
|
||||||
|
'username': retrieve_user,
|
||||||
|
'bar_code': retrieve_product,
|
||||||
|
'product_name': retrieve_product}
|
||||||
|
value_type = {'card': 'user',
|
||||||
|
'username': 'user',
|
||||||
|
'bar_code': 'product',
|
||||||
|
'product_name': 'product'}
|
||||||
|
value = retriever[typ](string, session)
|
||||||
|
# if value == None:
|
||||||
|
# print 'Input "%s" interpreted as %s; no matching %s found.' \
|
||||||
|
# % (string, typ, value_type[typ])
|
||||||
|
return (value_type[typ], value)
|
||||||
|
|
||||||
|
def search_ui(search_fun, search_str, thing, session):
|
||||||
|
result = search_fun(search_str, session)
|
||||||
|
if not isinstance(result, list):
|
||||||
|
return result
|
||||||
|
if len(result) == 0:
|
||||||
|
print 'No %ss matching "%s"' % (thing, search_str)
|
||||||
|
return None
|
||||||
|
if len(result) == 1:
|
||||||
|
msg = 'One %s matching "%s": %s. Use this? (y/n) ' %\
|
||||||
|
(thing, search_str, result[0])
|
||||||
|
if confirm(msg):
|
||||||
|
return result[0]
|
||||||
|
return None
|
||||||
|
selector = Selector('%d %ss matching "%s":' % (len(result), thing, search_str),
|
||||||
|
items=result,
|
||||||
|
prompt='select> ')
|
||||||
|
return selector.execute()
|
||||||
|
|
||||||
|
def retrieve_user(search_str, session):
|
||||||
|
return search_ui(search_user, search_str, 'user', session)
|
||||||
|
def retrieve_product(search_str, session):
|
||||||
|
return search_ui(search_product, search_str, 'product', session)
|
||||||
|
|
||||||
|
|
||||||
|
#main = MainMenu()
|
||||||
|
main = Menu('Dibbler main menu',
|
||||||
|
items=[BuyMenu(), ChargeMenu(), Menu('Add user'), Menu('Add product'),
|
||||||
|
ShowUserMenu(), ProductListMenu()],
|
||||||
|
exit_msg='happy happy joy joy')
|
||||||
main.execute()
|
main.execute()
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue