Noenlunde fungerende opplegg for kjøp, ymse små forbedringer.
* Ny tabell Transaction som brukes for alle transaksjoner; hver transaksjon kan være knyttet til en Purchase eller ha en tekstlig beskrivelse. (BuyMenu og TransferMenu viser de to måtene å bruke Transaction på) * Hvert kjøp kan ha flere brukere. Prisen fordeles likt blant kjøperne (for øyeblikket antar jeg at alle pengebeløp i databasen er lagret i kroner, og når totalprisen for et kjøp ikke går opp i antall kjøpere rundes det ned til et helt antall kroner) * Forbedret input i BuyMenu -- den gjetter på om man skriver inn et produkt eller en bruker basert på hvor den finner treff. (Hvis treffene er like gode begge steder velges det vilkårlig -- dette kan endres om det viser seg å være et problem i praksis) * BuyMenu lagrer faktisk kjøpene i databasen. * ShowUserMenu viser alle transaksjonene til brukeren. Dette kan bli mye etter hvert, så det bør sikkert begrenses på et eller annet vis (for eksempel at den bare viser de siste N, for et egnet naturlig tall N).
This commit is contained in:
parent
b9f5c39a76
commit
b648c27473
63
db.py
63
db.py
|
@ -54,41 +54,74 @@ class PurchaseEntry(Base):
|
||||||
def __init__(self, purchase, product, amount):
|
def __init__(self, purchase, product, amount):
|
||||||
self.product = product
|
self.product = product
|
||||||
self.product_bar_code = product.bar_code
|
self.product_bar_code = product.bar_code
|
||||||
self.purchase_id = purchase.id
|
self.purchase = purchase
|
||||||
self.amount = amount
|
self.amount = amount
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<PurchaseEntry('%s', '%s', '%s')>" % (self.purchase.user.user, self.product.name, self.amount )
|
return "<PurchaseEntry('%s', '%s', '%s')>" % (self.purchase.user.user, self.product.name, self.amount )
|
||||||
|
|
||||||
|
|
||||||
|
class Transaction(Base):
|
||||||
|
__tablename__ = 'transactions'
|
||||||
|
|
||||||
|
id = Column(Integer, primary_key=True)
|
||||||
|
time = Column(DateTime)
|
||||||
|
user_name = Column(String(10), ForeignKey('users.name'))
|
||||||
|
amount = Column(Integer)
|
||||||
|
description = Column(String(50))
|
||||||
|
purchase_id = Column(Integer, ForeignKey('purchases.id'))
|
||||||
|
|
||||||
|
user = relationship(User, backref=backref('transactions', order_by=time))
|
||||||
|
|
||||||
|
def __init__(self, user, amount=0, description=None, purchase=None):
|
||||||
|
self.user = user
|
||||||
|
self.amount = amount
|
||||||
|
self.description = description
|
||||||
|
self.purchase = purchase
|
||||||
|
|
||||||
|
def perform_transaction(self):
|
||||||
|
self.time = datetime.datetime.now()
|
||||||
|
self.user.credit -= self.amount
|
||||||
|
|
||||||
|
|
||||||
class Purchase(Base):
|
class Purchase(Base):
|
||||||
__tablename__ = 'purchases'
|
__tablename__ = 'purchases'
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True)
|
id = Column(Integer, primary_key=True)
|
||||||
time = Column(DateTime)
|
time = Column(DateTime)
|
||||||
user_name = Column(Integer, ForeignKey('users.name'))
|
# user_name = Column(Integer, ForeignKey('users.name'))
|
||||||
price = Column(Integer)
|
price = Column(Integer)
|
||||||
performed = Column(Boolean)
|
# performed = Column(Boolean)
|
||||||
|
|
||||||
user = relationship(User, backref=backref('purchases', order_by=id))
|
# user = relationship(User, backref=backref('purchases', order_by=id))
|
||||||
products = relationship(PurchaseEntry, backref=backref("purchase"))
|
# users = relationship(User, secondary=purchase_user, backref='purhcases'
|
||||||
|
transactions = relationship(Transaction, order_by=Transaction.user_name, backref='purchase')
|
||||||
|
entries = relationship(PurchaseEntry, backref=backref("purchase"))
|
||||||
|
|
||||||
def __init__(self ,performed=False):
|
def __init__(self):
|
||||||
self.performed = performed
|
pass
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<Purchase('%s', '%s', '%s')>" % (self.user.name, self.price, self.time.strftime('%c'))
|
return "<Purchase('%s', '%s', '%s')>" % (self.user.name, self.price, self.time.strftime('%c'))
|
||||||
|
|
||||||
|
def is_complete(self):
|
||||||
|
return len(self.transactions) > 0 and len(self.entries) > 0
|
||||||
|
|
||||||
|
def price_per_transaction(self):
|
||||||
|
return self.price/len(self.transactions)
|
||||||
|
|
||||||
def set_price(self):
|
def set_price(self):
|
||||||
self.price = 0
|
self.price = 0
|
||||||
for entry in self.products:
|
for entry in self.entries:
|
||||||
self.price += entry.amount*entry.product.price
|
self.price += entry.amount*entry.product.price
|
||||||
|
if len(self.transactions) > 0:
|
||||||
|
for t in self.transactions:
|
||||||
|
t.amount = self.price_per_transaction()
|
||||||
|
|
||||||
def perform_purchase(self):
|
def perform_purchase(self):
|
||||||
if self.performed:
|
self.time = datetime.datetime.now()
|
||||||
print "This transaction has already been performed"
|
self.set_price()
|
||||||
else:
|
for t in self.transactions:
|
||||||
self.time = datetime.datetime.now()
|
t.perform_transaction()
|
||||||
self.set_price()
|
# self.user.credit -= self.price
|
||||||
self.user.credit -= self.price
|
# self.performed = True
|
||||||
self.performed = True
|
|
||||||
|
|
15
helpers.py
15
helpers.py
|
@ -69,3 +69,18 @@ def select_from_list(list):
|
||||||
return list[int(choice)-1]
|
return list[int(choice)-1]
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def argmax(d, all=False, value=None):
|
||||||
|
maxarg = None
|
||||||
|
maxargs = []
|
||||||
|
if value != None:
|
||||||
|
dd = d
|
||||||
|
d = {}
|
||||||
|
for key in dd.keys():
|
||||||
|
d[key] = value(dd[key])
|
||||||
|
for key in d.keys():
|
||||||
|
if maxarg == None or d[key] > d[maxarg]:
|
||||||
|
maxarg = key
|
||||||
|
if all:
|
||||||
|
return filter(lambda k: d[k] == d[maxarg], d.keys())
|
||||||
|
return maxarg
|
||||||
|
|
190
text_based.py
190
text_based.py
|
@ -57,6 +57,53 @@ class Menu():
|
||||||
except ValueError:
|
except ValueError:
|
||||||
print 'Please enter an integer'
|
print 'Please enter an integer'
|
||||||
|
|
||||||
|
def input_user(self, prompt=None):
|
||||||
|
user = None
|
||||||
|
while user == None:
|
||||||
|
user = retrieve_user(self.input_str(prompt),
|
||||||
|
self.session)
|
||||||
|
return user
|
||||||
|
|
||||||
|
def input_product(self, prompt=None):
|
||||||
|
product = None
|
||||||
|
while product == None:
|
||||||
|
product = retrieve_product(self.input_str(prompt),
|
||||||
|
self.session)
|
||||||
|
return product
|
||||||
|
|
||||||
|
def input_thing(self, prompt=None, permitted_things=('user','product'),
|
||||||
|
empty_input_permitted=False):
|
||||||
|
result = None
|
||||||
|
while result == None:
|
||||||
|
search_str = self.input_str(prompt)
|
||||||
|
if search_str == '' and empty_input_permitted:
|
||||||
|
return None
|
||||||
|
result = self.search_for_thing(search_str, permitted_things)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def search_for_thing(self, search_str, permitted_things=('user','product')):
|
||||||
|
search_fun = {'user': search_user,
|
||||||
|
'product': search_product}
|
||||||
|
results = {}
|
||||||
|
result_values = {}
|
||||||
|
for thing in permitted_things:
|
||||||
|
results[thing] = search_fun[thing](search_str, self.session)
|
||||||
|
result_values[thing] = self.search_result_value(results[thing])
|
||||||
|
selected_thing = argmax(result_values)
|
||||||
|
return search_ui2(search_str, results[selected_thing],
|
||||||
|
selected_thing, self.session)
|
||||||
|
|
||||||
|
def search_result_value(self, result):
|
||||||
|
if result == None:
|
||||||
|
return 0
|
||||||
|
if not isinstance(result, list):
|
||||||
|
return 3
|
||||||
|
if len(result) == 0:
|
||||||
|
return 0
|
||||||
|
if len(result) == 1:
|
||||||
|
return 2
|
||||||
|
return 1
|
||||||
|
|
||||||
def print_header(self):
|
def print_header(self):
|
||||||
print
|
print
|
||||||
print '[%s]' % self.name
|
print '[%s]' % self.name
|
||||||
|
@ -136,21 +183,63 @@ class ChargeMenu(Menu):
|
||||||
self.session.close()
|
self.session.close()
|
||||||
break
|
break
|
||||||
|
|
||||||
|
class TransferMenu(Menu):
|
||||||
|
def __init__(self):
|
||||||
|
Menu.__init__(self, 'Transfer credit between users')
|
||||||
|
|
||||||
|
def _execute(self):
|
||||||
|
self.print_header()
|
||||||
|
self.session = Session()
|
||||||
|
amount = self.input_int('Transfer amount> ')
|
||||||
|
user1 = self.input_user('From user> ')
|
||||||
|
user2 = self.input_user('To user> ')
|
||||||
|
t1 = Transaction(user1, amount,
|
||||||
|
'Transfer to '+user2.name)
|
||||||
|
t2 = Transaction(user2, -amount,
|
||||||
|
'Transfer from '+user1.name)
|
||||||
|
t1.perform_transaction()
|
||||||
|
t2.perform_transaction()
|
||||||
|
self.session.add(t1)
|
||||||
|
self.session.add(t2)
|
||||||
|
self.session.commit()
|
||||||
|
print 'Transfered %d kr from %s to %s' % (amount, user1, user2)
|
||||||
|
print 'User %s\'s credit is now %d kr' % (user1, user1.credit)
|
||||||
|
print 'User %s\'s credit is now %d kr' % (user2, user2.credit)
|
||||||
|
self.session.close()
|
||||||
|
self.pause()
|
||||||
|
|
||||||
|
|
||||||
class ShowUserMenu(Menu):
|
class ShowUserMenu(Menu):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
Menu.__init__(self, 'Show user')
|
Menu.__init__(self, 'Show user')
|
||||||
|
|
||||||
def _execute(self):
|
def _execute(self):
|
||||||
|
self.session = Session()
|
||||||
self.print_header()
|
self.print_header()
|
||||||
user = None
|
user = self.input_user('User name or card number> ')
|
||||||
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 'User name: %s' % user.name
|
||||||
print 'Card number: %s' % user.card
|
print 'Card number: %s' % user.card
|
||||||
print 'Credit: %s' % user.credit
|
print 'Credit: %s' % user.credit
|
||||||
|
self.print_transactions(user)
|
||||||
self.pause()
|
self.pause()
|
||||||
|
|
||||||
|
def print_transactions(self, user):
|
||||||
|
if len(user.transactions) == 0:
|
||||||
|
print 'No transactions'
|
||||||
|
return
|
||||||
|
print 'Transactions:'
|
||||||
|
for t in user.transactions:
|
||||||
|
string = ' * %s: %d kr, ' % \
|
||||||
|
(t.time.strftime('%Y-%m-%d %H:%M'), t.amount)
|
||||||
|
if t.purchase:
|
||||||
|
string += 'purchase ('
|
||||||
|
string += ', '.join(map(lambda e: e.product.name,
|
||||||
|
t.purchase.entries))
|
||||||
|
string += ')'
|
||||||
|
else:
|
||||||
|
string += t.description
|
||||||
|
print string
|
||||||
|
|
||||||
|
|
||||||
class BuyMenu(Menu):
|
class BuyMenu(Menu):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
@ -158,49 +247,75 @@ class BuyMenu(Menu):
|
||||||
|
|
||||||
def _execute(self):
|
def _execute(self):
|
||||||
self.print_header()
|
self.print_header()
|
||||||
session = Session()
|
self.session = Session()
|
||||||
user = None
|
self.purchase = Purchase()
|
||||||
products = []
|
|
||||||
while True:
|
while True:
|
||||||
self.print_partial_purchase(user, products)
|
self.print_purchase()
|
||||||
print {(False,False): 'Enter user or product identification',
|
print {(False,False): 'Enter user or product identification',
|
||||||
(False,True): 'Enter user identification or more products',
|
(False,True): 'Enter user identification or more products',
|
||||||
(True,False): 'Enter product identification',
|
(True,False): 'Enter product identification or more users',
|
||||||
(True,True): 'Enter more products, or an empty line to confirm'
|
(True,True): 'Enter more products or users, or an empty line to confirm'
|
||||||
}[(user != None, len(products) > 0)]
|
}[(len(self.purchase.transactions) > 0,
|
||||||
string = self.input_str()
|
len(self.purchase.entries) > 0)]
|
||||||
if string == '':
|
thing = self.input_thing(empty_input_permitted=True)
|
||||||
if user == None or len(products) == 0:
|
if thing == None:
|
||||||
|
if not self.complete_input():
|
||||||
if confirm('Not enough information entered. Abort purchase? (y/n) '):
|
if confirm('Not enough information entered. Abort purchase? (y/n) '):
|
||||||
return False
|
return False
|
||||||
continue
|
continue
|
||||||
break
|
break
|
||||||
(value_type,value) = dwim_search(string, session)
|
if isinstance(thing, User):
|
||||||
if value != None:
|
Transaction(thing, purchase=self.purchase)
|
||||||
if value_type == 'user':
|
elif isinstance(thing, Product):
|
||||||
user = value
|
PurchaseEntry(self.purchase, thing, 1)
|
||||||
elif value_type == 'product':
|
|
||||||
products.append(value)
|
# for user in self.users:
|
||||||
print 'OK purchase'
|
# Transaction(user, purchase=self.purchase)
|
||||||
print self.format_partial_purchase(user, products)
|
# for product in self.products:
|
||||||
# TODO build Purchase object and commit
|
# PurchaseEntry(self.purchase, product, 1)
|
||||||
|
self.purchase.perform_purchase()
|
||||||
|
|
||||||
|
self.session.add(self.purchase)
|
||||||
|
self.session.commit()
|
||||||
|
|
||||||
|
print 'Purchase stored.' # TODO print info about purchase
|
||||||
|
self.print_purchase()
|
||||||
|
for t in self.purchase.transactions:
|
||||||
|
print 'User %s\'s credit is now %d kr' % (t.user.name, t.user.credit)
|
||||||
|
self.session.close()
|
||||||
self.pause()
|
self.pause()
|
||||||
|
return True
|
||||||
|
|
||||||
|
def complete_input(self):
|
||||||
|
return self.purchase.is_complete()
|
||||||
|
|
||||||
def format_partial_purchase(self, user, products):
|
def format_purchase(self):
|
||||||
if user == None and len(products) == 0:
|
self.purchase.set_price()
|
||||||
return
|
transactions = self.purchase.transactions
|
||||||
strings = []
|
entries = self.purchase.entries
|
||||||
if user != None:
|
if len(transactions) == 0 and len(entries) == 0:
|
||||||
strings.append(' user: ' + user.name)
|
return None
|
||||||
if len(products) > 0:
|
string = 'Purchase:'
|
||||||
strings.append(' products: ' + ', '.join(map(lambda p: p.name, products)))
|
string += '\n buyers: '
|
||||||
return '\n'.join(strings)
|
if len(transactions) == 0:
|
||||||
|
string += '(empty)'
|
||||||
|
else:
|
||||||
|
string += ', '.join(map(lambda t: t.user.name, transactions))
|
||||||
|
string += '\n products: '
|
||||||
|
if len(entries) == 0:
|
||||||
|
string += '(empty)'
|
||||||
|
else:
|
||||||
|
string += ', '.join(map(lambda e: '%s (%d kr)'%(e.product.name, e.product.price),
|
||||||
|
entries))
|
||||||
|
if len(transactions) > 1:
|
||||||
|
string += '\n price per person: %d kr' % self.purchase.price_per_transaction()
|
||||||
|
string += '\n total price: %d kr' % self.purchase.price
|
||||||
|
return string
|
||||||
|
|
||||||
def print_partial_purchase(self, user, products):
|
def print_purchase(self):
|
||||||
info = self.format_partial_purchase(user, products)
|
info = self.format_purchase()
|
||||||
if info != None:
|
if info != None:
|
||||||
print 'Your purchase:\n' + info
|
print info
|
||||||
|
|
||||||
|
|
||||||
class ProductListMenu(Menu):
|
class ProductListMenu(Menu):
|
||||||
|
@ -258,6 +373,9 @@ def dwim_search(string, session):
|
||||||
|
|
||||||
def search_ui(search_fun, search_str, thing, session):
|
def search_ui(search_fun, search_str, thing, session):
|
||||||
result = search_fun(search_str, session)
|
result = search_fun(search_str, session)
|
||||||
|
return search_ui2(search_str, result, thing, session)
|
||||||
|
|
||||||
|
def search_ui2(search_str, result, thing, session):
|
||||||
if not isinstance(result, list):
|
if not isinstance(result, list):
|
||||||
return result
|
return result
|
||||||
if len(result) == 0:
|
if len(result) == 0:
|
||||||
|
@ -282,7 +400,7 @@ def retrieve_product(search_str, session):
|
||||||
|
|
||||||
#main = MainMenu()
|
#main = MainMenu()
|
||||||
main = Menu('Dibbler main menu',
|
main = Menu('Dibbler main menu',
|
||||||
items=[BuyMenu(), ChargeMenu(), Menu('Add user'), Menu('Add product'),
|
items=[BuyMenu(), ChargeMenu(), TransferMenu(), Menu('Add user'), Menu('Add product'),
|
||||||
ShowUserMenu(), ProductListMenu()],
|
ShowUserMenu(), ProductListMenu()],
|
||||||
exit_msg='happy happy joy joy')
|
exit_msg='happy happy joy joy')
|
||||||
main.execute()
|
main.execute()
|
||||||
|
|
Loading…
Reference in New Issue